From patchwork Sun Jun 16 23:27:16 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mr Dash Four X-Patchwork-Id: 251747 X-Patchwork-Delegate: kadlec@blackhole.kfki.hu Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 9F85F2C0091 for ; Mon, 17 Jun 2013 09:27:28 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755460Ab3FPX11 (ORCPT ); Sun, 16 Jun 2013 19:27:27 -0400 Received: from mail-we0-f175.google.com ([74.125.82.175]:63917 "EHLO mail-we0-f175.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755408Ab3FPX11 (ORCPT ); Sun, 16 Jun 2013 19:27:27 -0400 Received: by mail-we0-f175.google.com with SMTP id t59so1886948wes.34 for ; Sun, 16 Jun 2013 16:27:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:cc:subject :references:in-reply-to:content-type:content-transfer-encoding; bh=tCMWG5bz3QLt9stFz8kGBi9zk2W21LjKtpZ2xQy+fjs=; b=Vwwej/L1BIwGcfRr7fWvSexiX7NbGb2dHBaCjCVsGkZi+MlnfO1yGdf5AVOodcRHy7 qJ74dYczkPmSXMzEq0FnwiCEhPi1Gkaan018CZHT7/8g7TcRgYUVHPY5bjIdvmKf/rJ/ ri0wESh4ZndnEDqpUdPbeSGPNqJ2O8he44ZMlWvsTzSwYmwxwMaFoPE9zjvFxUTRS/Wo +kHPtghZ2/+jbbL56nDDABTPthEyU94RnaLkoNFSQu0I1aXUu1QugD/b878ZphCdIEZc 4Cs8pHevzFBMXW0UF3sOda8JbjU/UdqbEkLANVaiA/YoBUgAcKjOpcu8xPpGKV/jotjr OlUQ== X-Received: by 10.180.100.35 with SMTP id ev3mr3502367wib.12.1371425245684; Sun, 16 Jun 2013 16:27:25 -0700 (PDT) Received: from [10.68.68.173] (cpc2-gill1-0-0-cust4.20-1.cable.virginmedia.com. [77.100.109.5]) by mx.google.com with ESMTPSA id en3sm18650918wid.1.2013.06.16.16.27.24 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sun, 16 Jun 2013 16:27:24 -0700 (PDT) Message-ID: <51BE49D4.1010901@googlemail.com> Date: Mon, 17 Jun 2013 00:27:16 +0100 From: Dash Four User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.23) Gecko/20090812 Thunderbird/2.0.0.23 MIME-Version: 1.0 To: Jozsef Kadlecsik CC: Pablo Neira Ayuso , Netfilter Core Team Subject: [PATCH v2 2/5] ipset: add "inner" flag implementation References: In-Reply-To: Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org This patch implements "inner" flag option in set iptables match, allowing matching based on the properties (source/destination IP address, protocol, port and so on) of the original (inner) connection in the event of the following ICMP[v4,v6] messages: ICMPv4 destination-unreachable (code 3); ICMPv4 source-quench (code 4); ICMPv4 time-exceeded (code 11); ICMPv6 destination-unreachable (code 1); ICMPv6 packet-too-big (code 2); ICMPv6 time-exceeded (code 3); Revision history: v1 * initial revision v2 * redundant code removed; * added a new header file (ip_set_icmp.h) with 2 inline functions, allowing access to the internal icmp header properties; * removed ip[46]inneraddr[ptr]functions as they are no longer needed * added new ipv[46]addr[ptr] and ip_set_get*port functions, the old functions are still preserved for backwards compatibility Signed-off-by: Dash Four --- kernel/include/linux/netfilter/ipset/ip_set.h | 66 ++++++++++++++ .../include/linux/netfilter/ipset/ip_set_getport.h | 16 ++++ kernel/include/linux/netfilter/ipset/ip_set_icmp.h | 91 ++++++++++++++++++++ kernel/include/uapi/linux/netfilter/ipset/ip_set.h | 2 + kernel/net/netfilter/ipset/ip_set_getport.c | 78 +++++++++++++---- 5 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 kernel/include/linux/netfilter/ipset/ip_set_icmp.h -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h index 8499e25..5b6ef72 100644 --- a/kernel/include/linux/netfilter/ipset/ip_set.h +++ b/kernel/include/linux/netfilter/ipset/ip_set.h @@ -17,9 +17,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include #define _IP_SET_MODULE_DESC(a, b, c) \ MODULE_DESCRIPTION(a " type of IP sets, revisions " b "-" c) @@ -361,6 +365,25 @@ static inline int nla_put_ipaddr6(struct sk_buff *skb, int type, } /* Get address from skbuff */ +static inline bool +ipv4addrptr(const struct sk_buff *skb, bool inner, bool src, __be32 *addr) +{ + struct iphdr *ih = ip_hdr(skb); + unsigned int protooff = ip_hdrlen(skb); + + if (ih == NULL || + (inner && + (ih->protocol != IPPROTO_ICMP || + !ip_set_get_icmpv4_inner_hdr(skb, &protooff, &ih)))) + goto err; + + *addr = src ? ih->saddr : ih->daddr; + return true; + +err: + return false; +} + static inline __be32 ip4addr(const struct sk_buff *skb, bool src) { @@ -373,12 +396,55 @@ ip4addrptr(const struct sk_buff *skb, bool src, __be32 *addr) *addr = src ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr; } +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static inline bool +ipv6addrptr(const struct sk_buff *skb, bool inner, bool src, + struct in6_addr *addr) +{ + struct ipv6hdr *ih = ipv6_hdr(skb); + + if (ih == NULL) + goto err; + + if (inner) { + unsigned int protooff; + u8 nexthdr = ih->nexthdr; + __be16 frag_off; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), + &nexthdr, &frag_off); +#else + protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), + &nexthdr); +#endif + if (protooff < 0 || nexthdr != IPPROTO_ICMPV6 || + !ip_set_get_icmpv6_inner_hdr(skb, &protooff, &ih)) + goto err; + } + memcpy(addr, src ? &ih->saddr : &ih->daddr, sizeof(*addr)); + return true; + +err: + return false; +} + static inline void ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) { memcpy(addr, src ? &ipv6_hdr(skb)->saddr : &ipv6_hdr(skb)->daddr, sizeof(*addr)); } +#else +static inline bool +ipv6addrptr(const struct sk_buff *skb, bool inner, bool src, + struct in6_addr *addr) +{ + return false; +} + +static inline void +ip6addrptr(const struct sk_buff *skb, bool src, struct in6_addr *addr) { ; } +#endif /* Calculate the bytes required to store the inclusive range of a-b */ static inline int diff --git a/kernel/include/linux/netfilter/ipset/ip_set_getport.h b/kernel/include/linux/netfilter/ipset/ip_set_getport.h index 90d0930..8cdc177 100644 --- a/kernel/include/linux/netfilter/ipset/ip_set_getport.h +++ b/kernel/include/linux/netfilter/ipset/ip_set_getport.h @@ -1,13 +1,26 @@ #ifndef _IP_SET_GETPORT_H #define _IP_SET_GETPORT_H +extern bool ip_set_get_ipv4_port(const struct sk_buff *skb, bool inner, + bool src, __be16 *port, u8 *proto); + extern bool ip_set_get_ip4_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto); #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +extern bool ip_set_get_ipv6_port(const struct sk_buff *skb, bool inner, + bool src, __be16 *port, u8 *proto); + extern bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto); #else +static inline bool ip_set_get_ipv6_port(const struct sk_buff *skb, + bool inner, bool src, + __be16 *port, u8 *proto) +{ + return false; +} + static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, __be16 *port, u8 *proto) { @@ -15,6 +28,9 @@ static inline bool ip_set_get_ip6_port(const struct sk_buff *skb, bool src, } #endif +extern bool ip_set_get_ipv_port(const struct sk_buff *skb, u8 pf, bool inner, + bool src, __be16 *port); + extern bool ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port); diff --git a/kernel/include/linux/netfilter/ipset/ip_set_icmp.h b/kernel/include/linux/netfilter/ipset/ip_set_icmp.h new file mode 100644 index 0000000..e116d28 --- /dev/null +++ b/kernel/include/linux/netfilter/ipset/ip_set_icmp.h @@ -0,0 +1,91 @@ +#ifndef _IP_SET_ICMP_H +#define _IP_SET_ICMP_H + +#include +#include +#include + +static inline +bool ip_set_get_icmpv4_inner_hdr(const struct sk_buff *skb, + unsigned int *offset, + struct iphdr **ih) +{ + u8 type; + struct iphdr _iph; + struct icmphdr _icmph; + struct iphdr *iph; + const struct icmphdr *ich; + /* RFC 1122: 3.2.2: req'd len: IP header + 8 bytes of inner header */ + static const size_t req_len = sizeof(struct iphdr) + 8; + + if (offset == NULL || ih == NULL) + goto err; + + ich = skb_header_pointer(skb, *offset, sizeof(_icmph), &_icmph); + if (ich == NULL || + (ich->type <= NR_ICMP_TYPES && skb->len - *offset < req_len)) + goto err; + + type = ich->type; + if (type == ICMP_DEST_UNREACH || + type == ICMP_SOURCE_QUENCH || + type == ICMP_TIME_EXCEEDED) { + *offset += sizeof(_icmph); + iph = skb_header_pointer(skb, *offset, sizeof(_iph), &_iph); + if (iph == NULL || ntohs(iph->frag_off) & IP_OFFSET) + goto err; + + *ih = iph; + return true; + } + +err: + return false; +} + +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +static inline +bool ip_set_get_icmpv6_inner_hdr(const struct sk_buff *skb, + unsigned int *offset, + struct ipv6hdr **ih) +{ + u8 type; + const struct icmp6hdr *ic; + struct icmp6hdr _icmp6h; + struct ipv6hdr _ip6h; + struct ipv6hdr *iph; + + if (offset == NULL || ih == NULL) + goto err; + + ic = skb_header_pointer(skb, *offset, sizeof(_icmp6h), &_icmp6h); + if (ic == NULL) + goto err; + + type = ic->icmp6_type; + if (type == ICMPV6_DEST_UNREACH || + type == ICMPV6_PKT_TOOBIG || + type == ICMPV6_TIME_EXCEED) { + *offset += sizeof(_icmp6h); + iph = skb_header_pointer(skb, *offset, sizeof(_ip6h), &_ip6h); + if (iph == NULL) + goto err; + + *ih = iph; + return true; + } + +err: + return false; +} +#else +static inline +bool ip_set_get_icmpv6_inner_hdr(const struct sk_buff *skb, + unsigned int offset, + struct ipv6hdr **ih) +{ + return false; +} +#endif + +#endif /*_IP_SET_ICMP_H*/ diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h index 8024cdf..e9e6586 100644 --- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h +++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h @@ -161,6 +161,8 @@ enum ipset_cmd_flags { (1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE), IPSET_FLAG_BIT_MATCH_COUNTERS = 5, IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS), + IPSET_FLAG_BIT_INNER = 6, + IPSET_FLAG_INNER = (1 << IPSET_FLAG_BIT_INNER), IPSET_FLAG_BIT_RETURN_NOMATCH = 7, IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH), IPSET_FLAG_CMD_MAX = 15, diff --git a/kernel/net/netfilter/ipset/ip_set_getport.c b/kernel/net/netfilter/ipset/ip_set_getport.c index a0d96eb..73ccfb3 100644 --- a/kernel/net/netfilter/ipset/ip_set_getport.c +++ b/kernel/net/netfilter/ipset/ip_set_getport.c @@ -19,7 +19,9 @@ #include #include #include +#include +#include #include /* We must handle non-linear skbs */ @@ -97,10 +99,10 @@ get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, } bool -ip_set_get_ip4_port(const struct sk_buff *skb, bool src, - __be16 *port, u8 *proto) +ip_set_get_ipv4_port(const struct sk_buff *skb, bool inner, bool src, + __be16 *port, u8 *proto) { - const struct iphdr *iph = ip_hdr(skb); + struct iphdr *iph = ip_hdr(skb); unsigned int protooff = ip_hdrlen(skb); int protocol = iph->protocol; @@ -116,54 +118,93 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src, case IPPROTO_UDPLITE: case IPPROTO_ICMP: /* Port info not available for fragment offset > 0 */ - return false; + goto err; default: /* Other protocols doesn't have ports, so we can match fragments */ *proto = protocol; return true; } + if (inner) { + if (protocol != IPPROTO_ICMP || + !ip_set_get_icmpv4_inner_hdr(skb, &protooff, &iph)) + goto err; + protocol = iph->protocol; + protooff += iph->ihl*4; + } return get_port(skb, protocol, protooff, src, port, proto); + +err: + return false; +} +EXPORT_SYMBOL_GPL(ip_set_get_ipv4_port); + +bool +ip_set_get_ip4_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + return ip_set_get_ipv4_port(skb, false, src, port, proto); } EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) bool -ip_set_get_ip6_port(const struct sk_buff *skb, bool src, - __be16 *port, u8 *proto) +ip_set_get_ipv6_port(const struct sk_buff *skb, bool inner, bool src, + __be16 *port, u8 *proto) { - int protoff; + unsigned int protooff; u8 nexthdr; __be16 frag_off = 0; nexthdr = ipv6_hdr(skb)->nexthdr; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) - protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, + protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, &frag_off); #else - protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); + protooff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); #endif - if (protoff < 0 || (frag_off & htons(~0x7)) != 0) - return false; + if (protooff < 0 || (frag_off & htons(~0x7)) != 0) + goto err; + + if (inner) { + struct ipv6hdr *ih; + if (nexthdr != IPPROTO_ICMPV6 || + !ip_set_get_icmpv6_inner_hdr(skb, &protooff, &ih)) + goto err; - return get_port(skb, nexthdr, protoff, src, port, proto); + nexthdr = ih->nexthdr; + protooff += sizeof(struct ipv6hdr); + } + return get_port(skb, nexthdr, protooff, src, port, proto); + +err: + return false; +} +EXPORT_SYMBOL_GPL(ip_set_get_ipv6_port); + +bool +ip_set_get_ip6_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + return ip_set_get_ipv6_port(skb, false, src, port, proto); } EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); #endif bool -ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) +ip_set_get_ipv_port(const struct sk_buff *skb, u8 pf, bool inner, bool src, + __be16 *port) { bool ret; u8 proto; switch (pf) { case NFPROTO_IPV4: - ret = ip_set_get_ip4_port(skb, src, port, &proto); + ret = ip_set_get_ipv4_port(skb, inner, src, port, &proto); break; case NFPROTO_IPV6: - ret = ip_set_get_ip6_port(skb, src, port, &proto); + ret = ip_set_get_ipv6_port(skb, inner, src, port, &proto); break; default: return false; @@ -178,4 +219,11 @@ ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) return false; } } +EXPORT_SYMBOL_GPL(ip_set_get_ipv_port); + +bool +ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) +{ + return ip_set_get_ipv_port(skb, pf, false, src, port); +} EXPORT_SYMBOL_GPL(ip_set_get_ip_port);