From 07d2ca530d353bc8a2b401674732ec3ba3d11977 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 20 Jan 2011 18:51:54 +0100 Subject: The get_ip*_port functions are too large to be inlined, moved into the core. --- kernel/ip_set_getport.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 kernel/ip_set_getport.c (limited to 'kernel/ip_set_getport.c') diff --git a/kernel/ip_set_getport.c b/kernel/ip_set_getport.c new file mode 100644 index 0000000..968c570 --- /dev/null +++ b/kernel/ip_set_getport.c @@ -0,0 +1,135 @@ +/* Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Get Layer-4 data from the packets */ + +#include +#include +#include +#include +#include +#include + +#include + +/* We must handle non-linear skbs */ +static bool +get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, + bool src, __be16 *port, u8 *proto) +{ + switch (protocol) { + case IPPROTO_TCP: { + struct tcphdr _tcph; + const struct tcphdr *th; + + th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); + if (th == NULL) + /* No choice either */ + return false; + + *port = src ? th->source : th->dest; + break; + } + case IPPROTO_UDP: { + struct udphdr _udph; + const struct udphdr *uh; + + uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); + if (uh == NULL) + /* No choice either */ + return false; + + *port = src ? uh->source : uh->dest; + break; + } + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph); + if (ic == NULL) + return false; + + *port = (__force __be16)((ic->type << 8) & ic->code); + break; + } + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmph; + const struct icmp6hdr *ic; + + ic = skb_header_pointer(skb, protooff, sizeof(_icmph), &_icmph); + if (ic == NULL) + return false; + + *port = (__force __be16)((ic->icmp6_type << 8) & ic->icmp6_code); + break; + } + default: + break; + } + *proto = protocol; + + return true; +} + +bool +ip_set_get_ip4_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + const struct iphdr *iph = ip_hdr(skb); + unsigned int protooff = ip_hdrlen(skb); + int protocol = iph->protocol; + + /* See comments at tcp_match in ip_tables.c */ + if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} +EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); + +bool +ip_set_get_ip6_port(const struct sk_buff *skb, bool src, + __be16 *port, u8 *proto) +{ + unsigned int protooff = 0; + int protocol; + unsigned short fragoff; + + protocol = ipv6_find_hdr(skb, &protooff, -1, &fragoff); + if (protocol <= 0 || fragoff) + return false; + + return get_port(skb, protocol, protooff, src, port, proto); +} +EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); + +bool +ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) +{ + bool ret; + u8 proto; + + switch (pf) { + case AF_INET: + ret = ip_set_get_ip4_port(skb, src, port, &proto); + case AF_INET6: + ret = ip_set_get_ip6_port(skb, src, port, &proto); + default: + return false; + } + if (!ret) + return ret; + switch (proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(ip_set_get_ip_port); -- cgit v1.2.3