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 2742741..0707188 100644
--- a/net.c
+++ b/net.c
@@ -929,6 +929,10 @@ static const struct {
.name = "drop",
.type = QEMU_OPT_STRING,
.help = "Enable the simple reverse firewall",
+ }, {
+ .name = "droplog",
+ .type = QEMU_OPT_STRING,
+ .help = "Set log filename for the reverse firewall",
},
{ /* end of list */ }
},
diff --git a/net/slirp.c b/net/slirp.c
index c0a3740..07e1353 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -141,7 +141,8 @@ 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, unsigned char drop)
+ const char *vsmbserver, unsigned char drop,
+ FILE *drop_log)
{
/* default settings according to historic slirp */
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
@@ -245,8 +246,8 @@ 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, drop, s);
+ s->slirp = slirp_init(restricted, net, mask, host, vhostname, tftp_export,
+ bootfile, dhcp, dns, drop, drop_log, s);
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
for (config = slirp_configs; config; config = config->next) {
@@ -690,10 +691,12 @@ int net_init_slirp(QemuOpts *opts,
const char *bootfile;
const char *smb_export;
const char *vsmbsrv;
+ const char *droplog_filename;
char *vnet = NULL;
int restricted = 0;
int ret;
unsigned char drop = 0;
+ FILE *drop_log = NULL;
vhost = qemu_opt_get(opts, "host");
vhostname = qemu_opt_get(opts, "hostname");
@@ -741,11 +744,20 @@ int net_init_slirp(QemuOpts *opts,
}
}
+ droplog_filename = qemu_opt_get(opts, "droplog");
+ if (droplog_filename) {
+ drop_log = fopen(droplog_filename, "w");
+ if (!drop_log) {
+ error_report("Unable to open reverse firewall log");
+ 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, drop);
+ vnamesrv, smb_export, vsmbsrv, drop, drop_log);
while (slirp_configs) {
config = slirp_configs;
diff --git a/qemu-options.hx b/qemu-options.hx
index ef3e726..7a8872b 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][,drop=udp|all]"
+ " [,hostfwd=rule][,guestfwd=rule][,drop=udp|all][,droplog=file]"
#ifndef _WIN32
"[,smb=dir[,smbserver=addr]]\n"
#endif
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 5778bf4..f1e48a7 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -15,7 +15,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
struct in_addr vnameserver, unsigned char drop,
- void *opaque);
+ FILE *drop_log, void *opaque);
void slirp_cleanup(Slirp *slirp);
void slirp_select_fill(int *pnfds,
@@ -48,6 +48,7 @@ size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
/* Reverse Firewall */
#define SLIRP_DROP_UDP 1
+int slirp_drop_log(FILE *drop_log, const char *format, ...);
int slirp_should_drop(Slirp *slirp,
struct in_addr dst_addr,
unsigned short dst_port,
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 298ccb4..81fd85b 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -200,7 +200,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, unsigned char drop, void *opaque)
+ struct in_addr vnameserver, unsigned char drop,
+ FILE *drop_log, void *opaque)
{
Slirp *slirp = qemu_mallocz(sizeof(Slirp));
@@ -231,6 +232,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->vnameserver_addr = vnameserver;
slirp->drop = drop;
+ slirp->drop_log = drop_log;
slirp->opaque = opaque;
@@ -248,6 +250,9 @@ void slirp_cleanup(Slirp *slirp)
unregister_savevm(NULL, "slirp", slirp);
+ if (slirp->drop_log) {
+ fclose(slirp->drop_log);
+ }
qemu_free(slirp->tftp_prefix);
qemu_free(slirp->bootp_filename);
qemu_free(slirp);
@@ -1114,6 +1119,9 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+/*
+ * Returns 1 if the packet should be dropped.
+ */
int slirp_should_drop(Slirp *slirp,
struct in_addr dst_addr,
unsigned short dst_port,
@@ -1130,3 +1138,23 @@ int slirp_should_drop(Slirp *slirp,
return 1;
}
+
+/*
+ * Write to drop-log
+ */
+int slirp_drop_log(FILE *drop_log, const char *format, ...)
+{
+ va_list args;
+
+ if (!drop_log) {
+ return 0;
+ }
+
+ va_start(args, format);
+ vfprintf(drop_log, format, args);
+ va_end(args);
+
+ fflush(drop_log);
+
+ return 1;
+}
diff --git a/slirp/slirp.h b/slirp/slirp.h
index bfea30d..d95953c 100644
--- a/slirp/slirp.h
+++ b/slirp/slirp.h
@@ -182,6 +182,7 @@ struct Slirp {
/* Reverse Firewall configuration */
unsigned char drop;
+ FILE *drop_log;
/* 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 95c4af0..6519d36 100644
--- a/slirp/udp.c
+++ b/slirp/udp.c
@@ -67,6 +67,8 @@ udp_input(register struct mbuf *m, int iphlen)
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("iphlen = %d", iphlen);
+ time_t timestamp = time(NULL);
+
/*
* Strip IP options, if any; should skip this,
* make available to user, and use on returned packets,
@@ -104,6 +106,14 @@ udp_input(register struct mbuf *m, int iphlen)
if (slirp_should_drop(
slirp, ip->ip_dst, uh->uh_dport, IPPROTO_UDP)) {
/* DROP */
+ slirp_drop_log(
+ slirp->drop_log,
+ "Dropped UDP: src:0x%08x:0x%04hx dst:0x%08x:0x%04hx %ld\n",
+ ntohl(ip->ip_src.s_addr),
+ ntohs(uh->uh_sport),
+ ntohl(ip->ip_dst.s_addr),
+ ntohs(uh->uh_dport),
+ timestamp);
goto bad;
} else {
/* PASS */