This patch series adds a simple reverse UDP firewall functionality to Slirp.
The series consists of three patches. Each adds one -net user option:
1. drop=udp|all - enables the firewall
2. droplog=FILE - sets the drop log filename
3. allow=PROTO:ADDR:PORT - adds an allow rule
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
If PROTO is omitted, all protocols match the rule.
TCP support will follow in another patch series.
diff --git a/net.c b/net.c
index 8d6a555..2742741 100644
--- a/net.c
+++ b/net.c
@@ -925,6 +925,10 @@ static const struct {
.name = "guestfwd",
.type = QEMU_OPT_STRING,
.help = "IP address and port to forward guest TCP connections",
+ }, {
+ .name = "drop",
+ .type = QEMU_OPT_STRING,
+ .help = "Enable the simple reverse firewall",
},
{ /* end of list */ }
},
diff --git a/net/slirp.c b/net/slirp.c
index b41c60a..c0a3740 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -141,7 +141,7 @@ static int net_slirp_init(VLANState *vlan, const char *model,
const char *vhostname, const char *tftp_export,
const char *bootfile, const char *vdhcp_start,
const char *vnameserver, const char *smb_export,
- const char *vsmbserver)
+ const char *vsmbserver, unsigned char drop)
{
/* default settings according to historic slirp */
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
@@ -246,7 +246,7 @@ static int net_slirp_init(VLANState *vlan, const char *model,
s = DO_UPCAST(SlirpState, nc, nc);
s->slirp = slirp_init(restricted, net, mask, host, vhostname,
- tftp_export, bootfile, dhcp, dns, s);
+ tftp_export, bootfile, dhcp, dns, drop, s);
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
for (config = slirp_configs; config; config = config->next) {
@@ -693,6 +693,7 @@ int net_init_slirp(QemuOpts *opts,
char *vnet = NULL;
int restricted = 0;
int ret;
+ unsigned char drop = 0;
vhost = qemu_opt_get(opts, "host");
vhostname = qemu_opt_get(opts, "hostname");
@@ -726,11 +727,25 @@ int net_init_slirp(QemuOpts *opts,
restricted = 1;
}
+ if (qemu_opt_get(opts, "drop")) {
+ switch (qemu_opt_get(opts, "drop")[0]) {
+ case 'u':
+ drop |= SLIRP_DROP_UDP;
+ break;
+ case 'a':
+ drop |= SLIRP_DROP_UDP;
+ break;
+ default:
+ error_report("Unknown protocol name given to drop option");
+ return -1;
+ }
+ }
+
qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
vhostname, tftp_export, bootfile, vdhcp_start,
- vnamesrv, smb_export, vsmbsrv);
+ vnamesrv, smb_export, vsmbsrv, drop);
while (slirp_configs) {
config = slirp_configs;
@@ -768,4 +783,3 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret
return 1;
}
-
diff --git a/qemu-options.hx b/qemu-options.hx
index ef60730..ef3e726 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1067,7 +1067,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
#ifdef CONFIG_SLIRP
"-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n"
" [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n"
- " [,hostfwd=rule][,guestfwd=rule]"
+ " [,hostfwd=rule][,guestfwd=rule][,drop=udp|all]"
#ifndef _WIN32
"[,smb=dir[,smbserver=addr]]\n"
#endif
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 67c70e3..5778bf4 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -14,7 +14,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
- struct in_addr vnameserver, void *opaque);
+ struct in_addr vnameserver, unsigned char drop,
+ void *opaque);
void slirp_cleanup(Slirp *slirp);
void slirp_select_fill(int *pnfds,
@@ -44,6 +45,14 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port);
+/* Reverse Firewall */
+#define SLIRP_DROP_UDP 1
+
+int slirp_should_drop(Slirp *slirp,
+ struct in_addr dst_addr,
+ unsigned short dst_port,
+ u_int8_t proto);
+
#else /* !CONFIG_SLIRP */
static inline void slirp_select_fill(int *pnfds, fd_set *readfds,
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 1593be1..298ccb4 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -200,7 +200,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
- struct in_addr vnameserver, void *opaque)
+ struct in_addr vnameserver, unsigned char drop, void *opaque)
{
Slirp *slirp = qemu_mallocz(sizeof(Slirp));
@@ -230,6 +230,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vdhcp_startaddr = vdhcp_start;
slirp->vnameserver_addr = vnameserver;
+ slirp->drop = drop;
+
slirp->opaque = opaque;
register_savevm(NULL, "slirp", 0, 3,
@@ -1111,3 +1113,20 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+
+int slirp_should_drop(Slirp *slirp,
+ struct in_addr dst_addr,
+ unsigned short dst_port,
+ u_int8_t proto) {
+ switch (proto) {
+ case IPPROTO_UDP:
+ if (!(slirp->drop & SLIRP_DROP_UDP)) {
+ return 0;
+ }
+ break;
+ default:
+ return 0; /* unrecognized protocol. default pass. */
+ }
+
+ return 1;
+}
diff --git a/slirp/slirp.h b/slirp/slirp.h
index 954289a..bfea30d 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -180,6 +180,9 @@ struct Slirp {
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
+ /* Reverse Firewall configuration */
+ unsigned char drop;
+
/* ARP cache for the guest IP addresses (XXX: allow many entries) */
uint8_t client_ethaddr[6];
diff --git a/slirp/udp.c b/slirp/udp.c
index 02b3793..95c4af0 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -98,6 +98,17 @@ udp_input(register struct mbuf *m, int iphlen)
ip->ip_len = len;
}
+ /*
+ * User mode firewall
+ */
+ if (slirp_should_drop(
+ slirp, ip->ip_dst, uh->uh_dport, IPPROTO_UDP)) {
+ /* DROP */
+ goto bad;
+ } else {
+ /* PASS */
+ }
+
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.