PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/jain-slee/resources/diameter-base/common/events/src/main/java/net/java/slee/resource/diameter/base/events/avp/IPFilterRule.java

http://mobicents.googlecode.com/
Java | 518 lines | 272 code | 52 blank | 194 comment | 70 complexity | 5ee160b007e73d0ff4ea9a6d99390127 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package net.java.slee.resource.diameter.base.events.avp;
  23. import java.util.regex.Pattern;
  24. import java.util.regex.Matcher;
  25. /**
  26. * Java class to represent the Diameter IPFilterRule AVP type.
  27. *<P>
  28. * The IPFilterRule format is derived from the OctetString AVP Base Format. It uses the ASCII charset.
  29. * Packets may be filtered based on the following information that is associated with it.
  30. *
  31. * <pre>
  32. * Direction (in or out)
  33. * Source and destination IP address (possibly masked)
  34. * Protocol
  35. * Source and destination port (lists or ranges)
  36. * TCP flags
  37. * IP fragment flag
  38. * IP options
  39. * ICMP types
  40. * </pre>
  41. *
  42. * Rules for the appropriate direction are evaluated in order, with
  43. * the first matched rule terminating the evaluation. Each packet is
  44. * evaluated once. If no rule matches, the packet is dropped if the
  45. * last rule evaluated was a permit, and passed if the last rule was
  46. * a deny.
  47. * <p/>
  48. * IPFilterRule filters MUST follow the format:
  49. * <pre>
  50. * action dir proto from src to dst [options]
  51. *
  52. * action permit - Allow packets that match the rule.
  53. * deny - Drop packets that match the rule.
  54. *
  55. * dir "in" is from the terminal, "out" is to the
  56. * terminal.
  57. *
  58. * proto An IP protocol specified by number. The "ip"
  59. * keyword means any protocol will match.
  60. *
  61. * src and dst &lt;address/mask&gt; [ports]
  62. *
  63. * The &lt;address/mask&gt; may be specified as:
  64. * ipno An IPv4 or IPv6 number in dotted-
  65. * quad or canonical IPv6 form. Only
  66. * this exact IP number will match the
  67. * rule.
  68. * ipno/bits An IP number as above with a mask
  69. * width of the form 1.2.3.4/24. In
  70. * this case, all IP numbers from
  71. * 1.2.3.0 to 1.2.3.255 will match.
  72. * The bit width MUST be valid for the
  73. * IP version and the IP number MUST
  74. * NOT have bits set beyond the mask.
  75. * For a match to occur, the same IP
  76. * version must be present in the
  77. * packet that was used in describing
  78. * the IP address. To test for a
  79. * particular IP version, the bits part
  80. * can be set to zero. The keyword
  81. * "any" is 0.0.0.0/0 or the IPv6
  82. * equivalent. The keyword "assigned"
  83. * is the address or set of addresses
  84. * assigned to the terminal. For IPv4,
  85. * a typical first rule is often "deny
  86. * in ip! assigned"
  87. *
  88. * The sense of the match can be inverted by
  89. * preceding an address with the not modifier (!),
  90. * causing all other addresses to be matched
  91. * instead. This does not affect the selection of
  92. * port numbers.
  93. *
  94. * With the TCP, UDP and SCTP protocols, optional
  95. * ports may be specified as:
  96. *
  97. * {port/port-port}[,ports[,...]]
  98. *
  99. * The '-' notation specifies a range of ports
  100. * (including boundaries).
  101. *
  102. * Fragmented packets that have a non-zero offset
  103. * (i.e., not the first fragment) will never match
  104. * a rule that has one or more port
  105. * specifications. See the frag option for
  106. * details on matching fragmented packets.
  107. *
  108. * options:
  109. * frag Match if the packet is a fragment and this is not
  110. * the first fragment of the datagram. frag may not
  111. * be used in conjunction with either tcpflags or
  112. * TCP/UDP port specifications.
  113. *
  114. * ipoptions spec
  115. * Match if the IP header contains the comma
  116. * separated list of options specified in spec. The
  117. * supported IP options are:
  118. *
  119. * ssrr (strict source route), lsrr (loose source
  120. * route), rr (record packet route) and ts
  121. * (timestamp). The absence of a particular option
  122. * may be denoted with a '!'.
  123. *
  124. * tcpoptions spec
  125. * Match if the TCP header contains the comma
  126. * separated list of options specified in spec. The
  127. * supported TCP options are:
  128. *
  129. * mss (maximum segment size), window (tcp window
  130. * advertisement), sack (selective ack), ts (rfc1323
  131. * timestamp) and cc (rfc1644 t/tcp connection
  132. * count). The absence of a particular option may
  133. * be denoted with a '!'.
  134. *
  135. * established
  136. * TCP packets only. Match packets that have the RST
  137. * or ACK bits set.
  138. *
  139. * setup TCP packets only. Match packets that have the SYN
  140. * bit set but no ACK bit.
  141. *
  142. * tcpflags spec
  143. * TCP packets only. Match if the TCP header
  144. * contains the comma separated list of flags
  145. * specified in spec. The supported TCP flags are:
  146. *
  147. * fin, syn, rst, psh, ack and urg. The absence of a
  148. * particular flag may be denoted with a '!'. A rule
  149. * that contains a tcpflags specification can never
  150. * match a fragmented packet that has a non-zero
  151. * offset. See the frag option for details on
  152. * matching fragmented packets.
  153. *
  154. * icmptypes types
  155. * ICMP packets only. Match if the ICMP type is in
  156. * the list types. The list may be specified as any
  157. * combination of ranges or individual types
  158. * separated by commas. Both the numeric values and
  159. * the symbolic values listed below can be used. The
  160. * supported ICMP types are:
  161. *
  162. * echo reply (0), destination unreachable (3),
  163. * source quench (4), redirect (5), echo request
  164. * (8), router advertisement (9), router
  165. * solicitation (10), time-to-live exceeded (11), IP
  166. * header bad (12), timestamp request (13),
  167. * timestamp reply (14), information request (15),
  168. * information reply (16), address mask request (17)
  169. * and address mask reply (18).
  170. * </pre>
  171. *
  172. * There is one kind of packet that the access device MUST always
  173. * discard, that is an IP fragment with a fragment offset of one. This
  174. * is a valid packet, but it only has one use, to try to circumvent
  175. * firewalls.
  176. * <p/>
  177. * An access device that is unable to interpret or apply a deny rule
  178. * MUST terminate the session. An access device that is unable to
  179. * interpret or apply a permit rule MAY apply a more restrictive
  180. * rule. An access device MAY apply deny rules of its own before the
  181. * supplied rules, for example to protect the access device owner's
  182. * infrastructure.
  183. * <p/>
  184. * The rule syntax is a modified subset of ipfw(8) from FreeBSD.
  185. *
  186. * @author Open Cloud
  187. * @author baranowb
  188. */
  189. public class IPFilterRule {
  190. public static final int ACTION_PERMIT = 0;
  191. public static final int ACTION_DENY = 1;
  192. public static final int DIR_IN = 0;
  193. public static final int DIR_OUT = 1;
  194. private static final String[] EMPTY_STRING_ARRAY = new String[0];
  195. private static final int[] EMPTY_INT_ARRAY = new int[0];
  196. private int action;
  197. private int direction;
  198. private boolean anyProtocol;
  199. private int protocol;
  200. private AddressSet sourceAddressSet;
  201. private AddressSet destAddressSet;
  202. private boolean fragment = false;
  203. private String ipOptions = null;
  204. private String tcpOptions = null;
  205. private boolean established = false;
  206. private boolean setup = false;
  207. private String tcpFlags = null;
  208. private String icmpTypes = null;
  209. public IPFilterRule(String rule) {
  210. parseRule(rule);
  211. }
  212. public String toString() {
  213. return getRuleString();
  214. }
  215. public String getRuleString() {
  216. StringBuffer ruleBuf = new StringBuffer();
  217. ruleBuf.append(action == ACTION_PERMIT ? "permit ":"deny ");
  218. ruleBuf.append(direction == DIR_IN ? "in ":"out ");
  219. ruleBuf.append(isAnyProtocol() ? "ip" : String.valueOf(protocol));
  220. ruleBuf.append(" from ");
  221. sourceAddressSet.appendAddressSet(ruleBuf);
  222. ruleBuf.append(" to ");
  223. destAddressSet.appendAddressSet(ruleBuf);
  224. ruleBuf.append(' ');
  225. ruleBuf.append(fragment ? "frag ":"");
  226. if(ipOptions != null) {
  227. ruleBuf.append("ipoptions ").append(ipOptions).append(' ');
  228. }
  229. if(tcpOptions != null) {
  230. ruleBuf.append("tcpoptions ").append(tcpOptions).append(' ');
  231. }
  232. ruleBuf.append(established ? "established ":"");
  233. ruleBuf.append(setup ? "setup ":"");
  234. if(tcpFlags != null) {
  235. ruleBuf.append("tcpflags ").append(tcpFlags).append(' ');;
  236. }
  237. if(icmpTypes != null) {
  238. ruleBuf.append("icmptypes ").append(icmpTypes);
  239. }
  240. return ruleBuf.toString();
  241. }
  242. public int getAction() {
  243. return action;
  244. }
  245. public int getDirection() {
  246. return direction;
  247. }
  248. public boolean isAnyProtocol() {
  249. return anyProtocol;
  250. }
  251. public int getProtocol() {
  252. return protocol;
  253. }
  254. public String getSourceIp() {
  255. return sourceAddressSet.ip;
  256. }
  257. public int getSourceBits() {
  258. return sourceAddressSet.bits;
  259. }
  260. public boolean isSourceAssignedIps() {
  261. return sourceAddressSet.assignedIps;
  262. }
  263. public boolean isSourceNoMatch() {
  264. return sourceAddressSet.notMatch;
  265. }
  266. public int[][] getSourcePorts() {
  267. return sourceAddressSet.ports;
  268. }
  269. public String getDestIp() {
  270. return destAddressSet.ip;
  271. }
  272. public int getDestBits() {
  273. return destAddressSet.bits;
  274. }
  275. public boolean isDestAssignedIps() {
  276. return destAddressSet.assignedIps;
  277. }
  278. public boolean isDestNoMatch() {
  279. return destAddressSet.notMatch;
  280. }
  281. public int[][] getDestPorts() {
  282. return destAddressSet.ports;
  283. }
  284. public boolean isFragment() {
  285. return fragment;
  286. }
  287. public String[] getIpOptions() {
  288. return ipOptions == null ? EMPTY_STRING_ARRAY : ipOptions.split(",");
  289. }
  290. public String[] getTcpOptions() {
  291. return tcpOptions == null ? EMPTY_STRING_ARRAY : tcpOptions.split(",");
  292. }
  293. public boolean isEstablished() {
  294. return established;
  295. }
  296. public boolean isSetup() {
  297. return setup;
  298. }
  299. public String[] getTcpFlags() {
  300. return tcpFlags == null ? EMPTY_STRING_ARRAY : tcpFlags.split(",");
  301. }
  302. public String[] getIcmpTypes() {
  303. return icmpTypes == null ? EMPTY_STRING_ARRAY : icmpTypes.split(",");
  304. }
  305. public int[] getNumericIcmpTypes() {
  306. // TODO: Implement Numeric ICMP Types
  307. return EMPTY_INT_ARRAY;
  308. }
  309. private void parseRule(String rule) {
  310. //THIS: \\s+(.+?)((frag|tcpoptions|setup|ipoptions|established|setup|tcpflags|icmptypes)(.+))? matches - everything, or everything up to keywords if they are there.
  311. //defines group 5 as everything and group 6 as leftover, aka options, which are optional. 6 has two subgroups, one to match keyword, second to swallow everything after keyword.
  312. //other way would be to match by word boundary - but would have to check if present word ia a port declaration or options. this seems better idea.
  313. Pattern ruleParser = Pattern.compile("(.+)\\s+(.+)\\s+(.+)\\s+from\\s+(.+)\\s+to\\s+(.+?)((frag|tcpoptions|setup|ipoptions|established|setup|tcpflags|icmptypes)(.*))?");
  314. Matcher matcher = ruleParser.matcher(rule.trim());
  315. if(matcher.matches()) {
  316. parseAction(matcher.group(1), rule);
  317. parseDirection(matcher.group(2), rule);
  318. parseProtocol(matcher.group(3), rule);
  319. parseFrom(matcher.group(4), rule);
  320. parseTo(matcher.group(5).trim(), rule); // trim is here to kill leftover white space if any.
  321. parseOptions(matcher.group(6), rule); // 6 is to match options if exist
  322. }
  323. else {
  324. fail(rule);
  325. }
  326. }
  327. private void parseProtocol(String proto, String rule) {
  328. if("ip".equals(proto)) {
  329. anyProtocol = true;
  330. }
  331. else {
  332. try {
  333. protocol = Integer.parseInt(proto);
  334. }
  335. catch (NumberFormatException nfe) {
  336. fail(rule, proto);
  337. }
  338. }
  339. }
  340. private void parseDirection(String dir, String rule) {
  341. if("in".equals(dir)) {
  342. direction = DIR_IN;
  343. }
  344. else if("out".equals(dir)) {
  345. direction = DIR_OUT;
  346. }
  347. else fail(rule, dir);
  348. }
  349. private void parseAction(String action, String rule) {
  350. if("permit".equals(action)) {
  351. this.action = ACTION_PERMIT;
  352. }
  353. else if("deny".equals(action)) {
  354. this.action = ACTION_DENY;
  355. }
  356. else fail(rule, action);
  357. }
  358. private void parseFrom(String from, String rule) {
  359. sourceAddressSet = parseAddressSet(from, rule);
  360. }
  361. private void parseTo(String to, String rule) {
  362. destAddressSet = parseAddressSet(to, rule);
  363. }
  364. private AddressSet parseAddressSet(String addressSetString, String rule) {
  365. AddressSet addressSet = new AddressSet();
  366. // MONSTER KILL: matches keywords, IPv4 and IPv6 address
  367. // this is actually a bit bad, allows any/24 for instance...
  368. // 1 2 22 23
  369. Pattern ipv4Pattern = Pattern.compile("(!?)(any|assigned|"+ipv4Regexp+"|"+ipv6Regexp+")(/[0-9]{1,3})?( [0-9,-]*)?");
  370. Matcher matcher = ipv4Pattern.matcher(addressSetString);
  371. if(matcher.matches()) {
  372. addressSet.notMatch = "!".equals(matcher.group(1));
  373. if("assigned".equals(matcher.group(2))) {
  374. addressSet.assignedIps = true;
  375. }
  376. else {
  377. addressSet.ip = matcher.group(2);
  378. if(null != matcher.group(22)) { //NOTE: 22, since regex for addresses have lots of groups :P
  379. try {
  380. addressSet.bits = Integer.parseInt(matcher.group(22).substring(1));
  381. }
  382. catch (NumberFormatException nfe) {
  383. fail(rule, matcher.group(22));
  384. }
  385. }
  386. }
  387. // {port | port-port}[,ports[,...]]
  388. if(null != matcher.group(23)) { //NOTE: 23- as above note
  389. String portsString = matcher.group(23).trim();
  390. String ports[] = portsString.split(",");
  391. addressSet.ports = new int[ports.length][2];
  392. for (int i = 0; i < ports.length; i++) {
  393. String port = ports[i];
  394. String[] ranges = port.split("-");
  395. addressSet.ports[i][0] = Integer.parseInt(ranges[0]);
  396. try {
  397. if(ranges.length == 1) {
  398. addressSet.ports[i][1] = addressSet.ports[i][0];
  399. }
  400. else if(ranges.length == 2) {
  401. addressSet.ports[i][1] = Integer.parseInt(ranges[1]);
  402. }
  403. else fail(rule, portsString);
  404. }
  405. catch (NumberFormatException e) {
  406. fail(rule, portsString);
  407. }
  408. }
  409. }
  410. }
  411. else fail(rule, addressSetString);
  412. return addressSet;
  413. }
  414. private void parseOptions(String options, String rule) {
  415. if(options != null && options.length() > 0) {
  416. Pattern optionsSplitter = Pattern.compile("\\s+");
  417. String[] optionsArray = optionsSplitter.split(options);
  418. for (int i = 0; i < optionsArray.length; i++) {
  419. String option = optionsArray[i];
  420. if("frag".equals(option)) fragment = true;
  421. else if("ipoptions".equals(option)) ipOptions = optionsArray[++i];
  422. else if("tcpoptions".equals(option)) tcpOptions = optionsArray[++i];
  423. else if("established".equals(option)) established = true;
  424. else if("setup".equals(option)) setup = true;
  425. else if("tcpflags".equals(option)) tcpFlags = optionsArray[++i];
  426. else if("icmptypes".equals(option)) icmpTypes = optionsArray[++i];
  427. else fail(rule, option);
  428. }
  429. }
  430. }
  431. private void fail(String rule, String error) {
  432. throw new IllegalArgumentException("Could not parse rule \"" + rule + "\", failed at: \"" + error + "\"");
  433. }
  434. private void fail(String rule) {
  435. throw new IllegalArgumentException("Could not parse rule \"" + rule + "\", failed to match.");
  436. }
  437. private class AddressSet {
  438. private void appendAddressSet(StringBuffer asBuf) {
  439. if(notMatch) asBuf.append('!');
  440. if(assignedIps) {
  441. asBuf.append("assigned");
  442. }
  443. else {
  444. asBuf.append(ip);
  445. if(0 <= bits) asBuf.append('/').append(bits);
  446. }
  447. if(null != ports) {
  448. asBuf.append(' ');
  449. for (int i = 0; i < ports.length; i++) {
  450. int[] sourcePort = ports[i];
  451. asBuf.append(sourcePort[0]);
  452. if(sourcePort[1] != sourcePort[0]) asBuf.append('-').append(sourcePort[1]);
  453. if(i != ports.length-1) asBuf.append(',');
  454. }
  455. }
  456. }
  457. private String ip;
  458. private int bits = -1;
  459. private int[][] ports;
  460. private boolean assignedIps = false;
  461. private boolean notMatch = false;
  462. }
  463. //some helper statics to make it cleaner
  464. private static final String ipv4Regexp = "(25[0-6]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2}).(25[0-6]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2}).(25[0-6]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2}).(25[0-6]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2})";
  465. private static final String ipv6Regexp = "((?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?))|(((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})";
  466. }