Umesh Krishnaswamy | 345ee99 | 2012-12-13 20:29:48 -0800 | [diff] [blame] | 1 | package net.floodlightcontroller.firewall; |
| 2 | |
| 3 | import java.io.IOException; |
| 4 | import java.util.Iterator; |
| 5 | import java.util.List; |
| 6 | |
| 7 | import org.codehaus.jackson.JsonParseException; |
| 8 | import org.codehaus.jackson.JsonParser; |
| 9 | import org.codehaus.jackson.JsonToken; |
| 10 | import org.codehaus.jackson.map.MappingJsonFactory; |
| 11 | import org.openflow.util.HexString; |
| 12 | import org.restlet.resource.Delete; |
| 13 | import org.restlet.resource.Post; |
| 14 | import org.restlet.resource.Get; |
| 15 | import org.restlet.resource.ServerResource; |
| 16 | import org.slf4j.Logger; |
| 17 | import org.slf4j.LoggerFactory; |
| 18 | |
| 19 | import net.floodlightcontroller.packet.Ethernet; |
| 20 | import net.floodlightcontroller.packet.IPv4; |
| 21 | |
| 22 | public class FirewallRulesResource extends ServerResource { |
| 23 | protected static Logger log = LoggerFactory.getLogger(FirewallRulesResource.class); |
| 24 | |
| 25 | @Get("json") |
| 26 | public Object handleRequest() { |
| 27 | IFirewallService firewall = |
| 28 | (IFirewallService)getContext().getAttributes(). |
| 29 | get(IFirewallService.class.getCanonicalName()); |
| 30 | |
| 31 | return firewall.getRules(); |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Takes a Firewall Rule string in JSON format and parses it into |
| 36 | * our firewall rule data structure, then adds it to the firewall. |
| 37 | * @param fmJson The Firewall rule entry in JSON format. |
| 38 | * @return A string status message |
| 39 | */ |
| 40 | @Post |
| 41 | public String store(String fmJson) { |
| 42 | IFirewallService firewall = |
| 43 | (IFirewallService)getContext().getAttributes(). |
| 44 | get(IFirewallService.class.getCanonicalName()); |
| 45 | |
| 46 | FirewallRule rule; |
| 47 | try { |
| 48 | rule = jsonToFirewallRule(fmJson); |
| 49 | } catch (IOException e) { |
| 50 | log.error("Error parsing firewall rule: " + fmJson, e); |
| 51 | e.printStackTrace(); |
| 52 | return "{\"status\" : \"Error! Could not parse firewall rule, see log for details.\"}"; |
| 53 | } |
| 54 | String status = null; |
| 55 | if (checkRuleExists(rule, firewall.getRules())) { |
| 56 | status = "Error! A similar firewall rule already exists."; |
| 57 | log.error(status); |
| 58 | } else { |
| 59 | // add rule to firewall |
| 60 | firewall.addRule(rule); |
| 61 | status = "Rule added"; |
| 62 | } |
| 63 | return ("{\"status\" : \"" + status + "\"}"); |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * Takes a Firewall Rule string in JSON format and parses it into |
| 68 | * our firewall rule data structure, then deletes it from the firewall. |
| 69 | * @param fmJson The Firewall rule entry in JSON format. |
| 70 | * @return A string status message |
| 71 | */ |
| 72 | |
| 73 | @Delete |
| 74 | public String remove(String fmJson) { |
| 75 | IFirewallService firewall = |
| 76 | (IFirewallService)getContext().getAttributes(). |
| 77 | get(IFirewallService.class.getCanonicalName()); |
| 78 | |
| 79 | FirewallRule rule; |
| 80 | try { |
| 81 | rule = jsonToFirewallRule(fmJson); |
| 82 | } catch (IOException e) { |
| 83 | log.error("Error parsing firewall rule: " + fmJson, e); |
| 84 | e.printStackTrace(); |
| 85 | return "{\"status\" : \"Error! Could not parse firewall rule, see log for details.\"}"; |
| 86 | } |
| 87 | String status = null; |
| 88 | boolean exists = false; |
| 89 | Iterator<FirewallRule> iter = firewall.getRules().iterator(); |
| 90 | while (iter.hasNext()) { |
| 91 | FirewallRule r = iter.next(); |
| 92 | if (r.ruleid == rule.ruleid) { |
| 93 | exists = true; |
| 94 | break; |
| 95 | } |
| 96 | } |
| 97 | if (!exists) { |
| 98 | status = "Error! Can't delete, a rule with this ID doesn't exist."; |
| 99 | log.error(status); |
| 100 | } else { |
| 101 | // delete rule from firewall |
| 102 | firewall.deleteRule(rule.ruleid); |
| 103 | status = "Rule deleted"; |
| 104 | } |
| 105 | return ("{\"status\" : \"" + status + "\"}"); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Turns a JSON formatted Firewall Rule string into a FirewallRule instance |
| 110 | * @param fmJson The JSON formatted static firewall rule |
| 111 | * @return The FirewallRule instance |
| 112 | * @throws IOException If there was an error parsing the JSON |
| 113 | */ |
| 114 | |
| 115 | public static FirewallRule jsonToFirewallRule(String fmJson) throws IOException { |
| 116 | FirewallRule rule = new FirewallRule(); |
| 117 | MappingJsonFactory f = new MappingJsonFactory(); |
| 118 | JsonParser jp; |
| 119 | |
| 120 | try { |
| 121 | jp = f.createJsonParser(fmJson); |
| 122 | } catch (JsonParseException e) { |
| 123 | throw new IOException(e); |
| 124 | } |
| 125 | |
| 126 | jp.nextToken(); |
| 127 | if (jp.getCurrentToken() != JsonToken.START_OBJECT) { |
| 128 | throw new IOException("Expected START_OBJECT"); |
| 129 | } |
| 130 | |
| 131 | while (jp.nextToken() != JsonToken.END_OBJECT) { |
| 132 | if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { |
| 133 | throw new IOException("Expected FIELD_NAME"); |
| 134 | } |
| 135 | |
| 136 | String n = jp.getCurrentName(); |
| 137 | jp.nextToken(); |
| 138 | if (jp.getText().equals("")) |
| 139 | continue; |
| 140 | |
| 141 | String tmp; |
| 142 | |
| 143 | // This is currently only applicable for remove(). In store(), ruleid takes a random number |
| 144 | if (n == "ruleid") { |
| 145 | rule.ruleid = Integer.parseInt((String)jp.getText()); |
| 146 | } |
| 147 | |
| 148 | // This assumes user having dpid info for involved switches |
| 149 | else if (n == "switchid") { |
| 150 | tmp = jp.getText(); |
| 151 | if (tmp.equalsIgnoreCase("-1") == false) { |
| 152 | // user inputs hex format dpid |
| 153 | rule.dpid = HexString.toLong(tmp); |
| 154 | rule.wildcard_dpid = false; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | else if (n == "src-inport") { |
| 159 | rule.in_port = Short.parseShort(jp.getText()); |
| 160 | rule.wildcard_in_port = false; |
| 161 | } |
| 162 | |
| 163 | else if (n == "src-mac") { |
| 164 | tmp = jp.getText(); |
| 165 | if (tmp.equalsIgnoreCase("ANY") == false) { |
| 166 | rule.wildcard_dl_src = false; |
| 167 | rule.dl_src = Ethernet.toLong(Ethernet.toMACAddress(tmp)); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | else if (n == "dst-mac") { |
| 172 | tmp = jp.getText(); |
| 173 | if (tmp.equalsIgnoreCase("ANY") == false) { |
| 174 | rule.wildcard_dl_dst = false; |
| 175 | rule.dl_dst = Ethernet.toLong(Ethernet.toMACAddress(tmp)); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | else if (n == "dl-type") { |
| 180 | tmp = jp.getText(); |
| 181 | if (tmp.equalsIgnoreCase("ARP")) { |
| 182 | rule.wildcard_dl_type = false; |
| 183 | rule.dl_type = Ethernet.TYPE_ARP; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | else if (n == "src-ip") { |
| 188 | tmp = jp.getText(); |
| 189 | if (tmp.equalsIgnoreCase("ANY") == false) { |
| 190 | rule.wildcard_nw_src = false; |
| 191 | rule.wildcard_dl_type = false; |
| 192 | rule.dl_type = Ethernet.TYPE_IPv4; |
| 193 | int[] cidr = IPCIDRToPrefixBits(tmp); |
| 194 | rule.nw_src_prefix = cidr[0]; |
| 195 | rule.nw_src_maskbits = cidr[1]; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | else if (n == "dst-ip") { |
| 200 | tmp = jp.getText(); |
| 201 | if (tmp.equalsIgnoreCase("ANY") == false) { |
| 202 | rule.wildcard_nw_dst = false; |
| 203 | rule.wildcard_dl_type = false; |
| 204 | rule.dl_type = Ethernet.TYPE_IPv4; |
| 205 | int[] cidr = IPCIDRToPrefixBits(tmp); |
| 206 | rule.nw_dst_prefix = cidr[0]; |
| 207 | rule.nw_dst_maskbits = cidr[1]; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | else if (n == "nw-proto") { |
| 212 | tmp = jp.getText(); |
| 213 | if (tmp.equalsIgnoreCase("TCP")) { |
| 214 | rule.wildcard_nw_proto = false; |
| 215 | rule.nw_proto = IPv4.PROTOCOL_TCP; |
| 216 | rule.wildcard_dl_type = false; |
| 217 | rule.dl_type = Ethernet.TYPE_IPv4; |
| 218 | } else if (tmp.equalsIgnoreCase("UDP")) { |
| 219 | rule.wildcard_nw_proto = false; |
| 220 | rule.nw_proto = IPv4.PROTOCOL_UDP; |
| 221 | rule.wildcard_dl_type = false; |
| 222 | rule.dl_type = Ethernet.TYPE_IPv4; |
| 223 | } else if (tmp.equalsIgnoreCase("ICMP")) { |
| 224 | rule.wildcard_nw_proto = false; |
| 225 | rule.nw_proto = IPv4.PROTOCOL_ICMP; |
| 226 | rule.wildcard_dl_type = false; |
| 227 | rule.dl_type = Ethernet.TYPE_IPv4; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | else if (n == "tp-src") { |
| 232 | rule.wildcard_tp_src = false; |
| 233 | rule.tp_src = Short.parseShort(jp.getText()); |
| 234 | } |
| 235 | |
| 236 | else if (n == "tp-dst") { |
| 237 | rule.wildcard_tp_dst = false; |
| 238 | rule.tp_dst = Short.parseShort(jp.getText()); |
| 239 | } |
| 240 | |
| 241 | else if (n == "priority") { |
| 242 | rule.priority = Integer.parseInt(jp.getText()); |
| 243 | } |
| 244 | |
| 245 | else if (n == "action") { |
| 246 | if (jp.getText().equalsIgnoreCase("allow") == true) { |
| 247 | rule.action = FirewallRule.FirewallAction.ALLOW; |
| 248 | } else if (jp.getText().equalsIgnoreCase("deny") == true) { |
| 249 | rule.action = FirewallRule.FirewallAction.DENY; |
| 250 | } |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | return rule; |
| 255 | } |
| 256 | |
| 257 | public static int[] IPCIDRToPrefixBits(String cidr) { |
| 258 | int ret[] = new int[2]; |
| 259 | |
| 260 | // as IP can also be a prefix rather than an absolute address |
| 261 | // split it over "/" to get the bit range |
| 262 | String[] parts = cidr.split("/"); |
| 263 | String cidr_prefix = parts[0].trim(); |
| 264 | int cidr_bits = 0; |
| 265 | if (parts.length == 2) { |
| 266 | try { |
| 267 | cidr_bits = Integer.parseInt(parts[1].trim()); |
| 268 | } catch (Exception exp) { |
| 269 | cidr_bits = 32; |
| 270 | } |
| 271 | } |
| 272 | ret[0] = IPv4.toIPv4Address(cidr_prefix); |
| 273 | ret[1] = cidr_bits; |
| 274 | |
| 275 | return ret; |
| 276 | } |
| 277 | |
| 278 | public static boolean checkRuleExists(FirewallRule rule, List<FirewallRule> rules) { |
| 279 | Iterator<FirewallRule> iter = rules.iterator(); |
| 280 | while (iter.hasNext()) { |
| 281 | FirewallRule r = iter.next(); |
| 282 | |
| 283 | // check if we find a similar rule |
| 284 | if (rule.isSameAs(r)) { |
| 285 | return true; |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | // no rule matched, so it doesn't exist in the rules |
| 290 | return false; |
| 291 | } |
| 292 | } |