Patchwork [4/4] Slirp Reverse UDP Firewall

login
register
mail settings
Submitter Daisuke Nojiri
Date April 16, 2011, 1:40 a.m.
Message ID <BANLkTi=K2eRm4pVi1sLdebmLqPu+AZB5Xg@mail.gmail.com>
Download mbox | patch
Permalink /patch/91466/
State New
Headers show

Comments

Daisuke Nojiri - April 16, 2011, 1:40 a.m.
This patch series adds a simple reverse UDP firewall functionality to Slirp.
The series consists of four patches:

    1. drop=udp|all - enables the firewall
    2. droplog=FILE - sets the drop log filename
    3. allow=PROTO:ADDR:PORT - adds an allow rule
    4. parse network mask (e.g. /24) for ADDR

  e.g.) $ qemu -net user,drop=udp,droplog=qemu.drop,allow=udp:10.0.2.3:53

All UDP packets except ones allowed by allow rules will be dropped.
The source and the destination of the dropped packets are logged in the file
specified by FILE. PORT can be a single number (e.g. 53) or a range
(e.g. [80-81]). ADDR can be a single address (e.g. 1.2.3.4) or a range
(e.g. 1.2.3.4/24). If ADDR is ommitted, all addresses match the rule.

TCP support will follow in another patch series.

Signed-off-by: Daisuke Nojiri <dnojiri@google.com>

Patch

diff --git a/net/slirp.c b/net/slirp.c
index 51e4728..a22ca5c 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -142,6 +142,66 @@  static NetClientInfo net_slirp_info = {
     .cleanup = net_slirp_cleanup,
 };

+/*
+ * Parse network address in CIDR notation (e.g. 1.2.3.4/24).
+ *
+ * If mask notation is not found (e.g. 1.2.3.4), it copies default_mask to
+ * mask. If default_mask is NULL, it automatically sets mask based on the
+ * discovered address.
+ */
+static int parse_network_address(const char *network,
+                                  struct in_addr *net,
+                                  struct in_addr *mask,
+                                  struct in_addr *default_mask)
+{
+    char buf[20];
+    uint32_t addr;
+    char *end;
+    int shift;
+
+    if (get_str_sep(buf, sizeof(buf), &network, '/') < 0) {
+        if (!inet_aton(network, net)) {
+            return -1;
+        }
+        if (default_mask != NULL) {
+            mask->s_addr = default_mask->s_addr;
+        } else {
+            addr = ntohl(net->s_addr);
+            if (!(addr & 0x80000000)) {
+                mask->s_addr = htonl(0xff000000); /* class A */
+            } else if ((addr & 0xfff00000) == 0xac100000) {
+                mask->s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
+            } else if ((addr & 0xc0000000) == 0x80000000) {
+                mask->s_addr = htonl(0xffff0000); /* class B */
+            } else if ((addr & 0xffff0000) == 0xc0a80000) {
+                mask->s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16*/
+            } else if ((addr & 0xffff0000) == 0xc6120000) {
+                mask->s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
+            } else if ((addr & 0xe0000000) == 0xe0000000) {
+                mask->s_addr = htonl(0xffffff00); /* class C */
+            } else {
+                mask->s_addr = htonl(0xfffffff0); /* multicast/reserved */
+            }
+        }
+    } else {
+        if (!inet_aton(buf, net)) {
+            return -1;
+        }
+        shift = strtol(network, &end, 10);
+        if (*end != '\0') {
+            if (!inet_aton(network, mask)) {
+                return -1;
+            }
+        } else if (shift < 4 || shift > 32) {
+            return -1;
+        } else {
+            mask->s_addr = htonl(0xffffffff << (32 - shift));
+        }
+    }
+    net->s_addr &= mask->s_addr;
+    return 0;
+}
+
 static int net_slirp_init(VLANState *vlan, const char *model,
                           const char *name, int restricted,
                           const char *vnetwork, const char *vhost,
@@ -162,10 +222,6 @@  static int net_slirp_init(VLANState *vlan, const char
*model,
 #endif
     VLANClientState *nc;
     SlirpState *s;
-    char buf[20];
-    uint32_t addr;
-    int shift;
-    char *end;
     struct slirp_config_str *config;

     if (!tftp_export) {
@@ -176,42 +232,9 @@  static int net_slirp_init(VLANState *vlan, const char
*model,
     }

     if (vnetwork) {
-        if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
-            if (!inet_aton(vnetwork, &net)) {
-                return -1;
-            }
-            addr = ntohl(net.s_addr);
-            if (!(addr & 0x80000000)) {
-                mask.s_addr = htonl(0xff000000); /* class A */
-            } else if ((addr & 0xfff00000) == 0xac100000) {
-                mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
-            } else if ((addr & 0xc0000000) == 0x80000000) {
-                mask.s_addr = htonl(0xffff0000); /* class B */
-            } else if ((addr & 0xffff0000) == 0xc0a80000) {
-                mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
-            } else if ((addr & 0xffff0000) == 0xc6120000) {
-                mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
-            } else if ((addr & 0xe0000000) == 0xe0000000) {
-                mask.s_addr = htonl(0xffffff00); /* class C */
-            } else {
-                mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
-            }
-        } else {
-            if (!inet_aton(buf, &net)) {
-                return -1;
-            }
-            shift = strtol(vnetwork, &end, 10);
-            if (*end != '\0') {
-                if (!inet_aton(vnetwork, &mask)) {
-                    return -1;
-                }
-            } else if (shift < 4 || shift > 32) {
-                return -1;
-            } else {
-                mask.s_addr = htonl(0xffffffff << (32 - shift));
-            }
+        if(parse_network_address(vnetwork, &net, &mask, NULL)) {
+            return -1;
         }
-        net.s_addr &= mask.s_addr;
         host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
         dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
         dns.s_addr  = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
@@ -857,10 +880,11 @@  int slirp_add_allow(Slirp *slirp, const char *optarg)
      */
     char *argument = strdup(optarg), *p = argument;
     char *dst_addr_str, *dst_port_str;
-    struct in_addr dst_addr;
+    struct in_addr dst_addr, dst_mask;
     unsigned short dst_lport, dst_hport;
     char *proto_str;
     u_int8_t proto;
+    struct in_addr default_mask = { .s_addr = htonl(0xffffffff) };

     proto_str = strsep(&p, ":");
     if (!strcmp(proto_str, "udp")) {
@@ -887,9 +911,11 @@  int slirp_add_allow(Slirp *slirp, const char *optarg)
     /* handling ":port" notation (when IP address is omitted entirely). */
     if (*dst_addr_str == '\0') {
         dst_addr.s_addr = 0;
-    } else if (inet_aton(dst_addr_str, &dst_addr) == 0) {
-        fprintf(stderr, "Invalid destination IP address: %s\n",
dst_addr_str);
-        return -1;
+    } else {
+        if (parse_network_address(
+            dst_addr_str, &dst_addr, &dst_mask, &default_mask)) {
+            return -1;
+        }
     }

     if (parse_port_range(dst_port_str, &dst_lport, &dst_hport) == -1) {
@@ -899,7 +925,8 @@  int slirp_add_allow(Slirp *slirp, const char *optarg)
         return -1;
     }

-    slirp_add_allow_internal(slirp, dst_addr, dst_lport, dst_hport, proto);
+    slirp_add_allow_internal(
+        slirp, dst_addr, dst_mask, dst_lport, dst_hport, proto);

     free(argument);
     return 0;
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 1f4ddb1..9e95715 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -54,9 +54,9 @@  int slirp_should_drop(Slirp *slirp,
                       unsigned short dst_port,
                       u_int8_t proto);

-/* slirp.c */
 void slirp_add_allow_internal(Slirp *slirp,
                               struct in_addr dst_addr,
+                              struct in_addr dst_mask,
                               unsigned short dst_lport,
                               unsigned short dst_hport,
                               u_int8_t proto);
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 928025e..e00e9f6 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -1149,8 +1149,9 @@  int slirp_should_drop(Slirp *slirp,
         dport = ntohs(dst_port);
         if ((allow->dst_lport <= dport) && (dport <= allow->dst_hport)) {
             /* allow any destination if 0 */
-            if (allow->dst_addr.s_addr == 0
-                || allow->dst_addr.s_addr == dst_addr.s_addr) {
+            if (allow->dst_addr.s_addr == 0 ||
+                allow->dst_addr.s_addr ==
+                (dst_addr.s_addr & allow->dst_mask.s_addr)) {
                 return 0;
             }
         }
@@ -1163,12 +1164,14 @@  int slirp_should_drop(Slirp *slirp,
  */
 void slirp_add_allow_internal(Slirp *slirp,
                               struct in_addr dst_addr,
+                              struct in_addr dst_mask,
                               unsigned short dst_lport,
                               unsigned short dst_hport,
                               u_int8_t proto) {
     struct rfw_allow *allow = qemu_malloc(sizeof(struct rfw_allow));

     allow->dst_addr = dst_addr;
+    allow->dst_mask = dst_mask;
     allow->dst_lport = dst_lport;
     allow->dst_hport = dst_hport;

diff --git a/slirp/slirp.h b/slirp/slirp.h
index 619bbee..a5c467f 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -175,6 +175,7 @@  struct rfw_allow {
     QSIMPLEQ_ENTRY(rfw_allow) next;

     struct in_addr dst_addr;
+    struct in_addr dst_mask;
     /* Port range. For a single port, dst_lport = dst_hport. */
     unsigned short dst_lport;   /* in host byte order */
     unsigned short dst_hport;   /* in host byte order */