From patchwork Sat Apr 16 01:40:10 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daisuke Nojiri X-Patchwork-Id: 91466 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8A268B6FBD for ; Sat, 16 Apr 2011 11:40:31 +1000 (EST) Received: from localhost ([::1]:60091 helo=lists2.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QAuUu-0003QP-T5 for incoming@patchwork.ozlabs.org; Fri, 15 Apr 2011 21:40:28 -0400 Received: from eggs.gnu.org ([140.186.70.92]:45957) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QAuUk-0003Q4-60 for qemu-devel@nongnu.org; Fri, 15 Apr 2011 21:40:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QAuUh-0002OE-Vm for qemu-devel@nongnu.org; Fri, 15 Apr 2011 21:40:18 -0400 Received: from smtp-out.google.com ([74.125.121.67]:59906) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QAuUh-0002OA-FC for qemu-devel@nongnu.org; Fri, 15 Apr 2011 21:40:15 -0400 Received: from kpbe19.cbf.corp.google.com (kpbe19.cbf.corp.google.com [172.25.105.83]) by smtp-out.google.com with ESMTP id p3G1eDrx001748 for ; Fri, 15 Apr 2011 18:40:14 -0700 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=google.com; s=beta; t=1302918014; bh=AIAYvXVLBIL6FuZ4tU6RDONQejE=; h=MIME-Version:Date:Message-ID:Subject:From:To:Content-Type; b=uVKiHw61jXAc6dS9fvWdlpgWi436UWkz/4X1G/Qi44MW24sjYXahG1DeyYbxIch9h Qojg/TLCOiU6JaavdvEnA== Received: from pvg4 (pvg4.prod.google.com [10.241.210.132]) by kpbe19.cbf.corp.google.com with ESMTP id p3G1eBx0026120 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Fri, 15 Apr 2011 18:40:11 -0700 Received: by pvg4 with SMTP id 4so1805284pvg.0 for ; Fri, 15 Apr 2011 18:40:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=beta; h=domainkey-signature:mime-version:date:message-id:subject:from:to :content-type; bh=54vW1x6eP51ew9x8dfsLFTruuHOVlQMXykTKrGlsk/k=; b=QjCHSUkdiAkyiZSIcMh7uS1cuA9r0uFHAlxUCmIcOXthVZamioITMTs2fwvSFhsp1x 4bYZPVhlG3d+kE5tFg1Q== DomainKey-Signature: a=rsa-sha1; c=nofws; d=google.com; s=beta; h=mime-version:date:message-id:subject:from:to:content-type; b=AGElrfBiU4wRqL4DO27MupioC5Z9TLS/wOyv7bKs4Nkebr0AiH79HvE3Sb7xz3q8lh DCAJhrOVojS5GcW0HMtg== MIME-Version: 1.0 Received: by 10.142.178.6 with SMTP id a6mr733770wff.58.1302918010754; Fri, 15 Apr 2011 18:40:10 -0700 (PDT) Received: by 10.142.188.11 with HTTP; Fri, 15 Apr 2011 18:40:10 -0700 (PDT) Date: Fri, 15 Apr 2011 18:40:10 -0700 Message-ID: From: Daisuke Nojiri To: qemu-devel@nongnu.org, Blue Swirl , Jan Kiszka X-System-Of-Record: true X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 74.125.121.67 Subject: [Qemu-devel] [PATCH 4/4] Slirp Reverse UDP Firewall X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 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 */