This patch series adds a simple reverse UDP firewall functionality to Slirp.
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
All UDP packets except ones allowed by allow rules will be dropped.
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
TCP support will follow in another patch series.
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) {
+ } else if ((addr & 0xc0000000) == 0x80000000) {
+ mask->s_addr = htonl(0xffff0000); /* class B */
+ } else if ((addr & 0xffff0000) == 0xc0a80000) {
+ } else if ((addr & 0xffff0000) == 0xc6120000) {
+ } 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) {
- } else if ((addr & 0xc0000000) == 0x80000000) {
- mask.s_addr = htonl(0xffff0000); /* class B */
- } else if ((addr & 0xffff0000) == 0xc0a80000) {
- } else if ((addr & 0xffff0000) == 0xc6120000) {
- } 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 */