1 /* Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 as 5 * published by the Free Software Foundation. 6 */ 7 8 /* Get Layer-4 data from the packets */ 9 10 #include <linux/ip.h> 11 #include <linux/skbuff.h> 12 #include <linux/icmp.h> 13 #include <linux/icmpv6.h> 14 #include <linux/netfilter_ipv6/ip6_tables.h> 15 #include <net/ip.h> 16 #include <net/ipv6.h> 17 18 #include <linux/netfilter/ipset/ip_set_getport.h> 19 20 /* We must handle non-linear skbs */ 21 static bool 22 get_port(const struct sk_buff *skb, int protocol, unsigned int protooff, 23 bool src, __be16 *port, u8 *proto) 24 { 25 switch (protocol) { 26 case IPPROTO_TCP: { 27 struct tcphdr _tcph; 28 const struct tcphdr *th; 29 30 th = skb_header_pointer(skb, protooff, sizeof(_tcph), &_tcph); 31 if (th == NULL) 32 /* No choice either */ 33 return false; 34 35 *port = src ? th->source : th->dest; 36 break; 37 } 38 case IPPROTO_UDP: { 39 struct udphdr _udph; 40 const struct udphdr *uh; 41 42 uh = skb_header_pointer(skb, protooff, sizeof(_udph), &_udph); 43 if (uh == NULL) 44 /* No choice either */ 45 return false; 46 47 *port = src ? uh->source : uh->dest; 48 break; 49 } 50 case IPPROTO_ICMP: { 51 struct icmphdr _ich; 52 const struct icmphdr *ic; 53 54 ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); 55 if (ic == NULL) 56 return false; 57 58 *port = (__force __be16)htons((ic->type << 8) | ic->code); 59 break; 60 } 61 case IPPROTO_ICMPV6: { 62 struct icmp6hdr _ich; 63 const struct icmp6hdr *ic; 64 65 ic = skb_header_pointer(skb, protooff, sizeof(_ich), &_ich); 66 if (ic == NULL) 67 return false; 68 69 *port = (__force __be16) 70 htons((ic->icmp6_type << 8) | ic->icmp6_code); 71 break; 72 } 73 default: 74 break; 75 } 76 *proto = protocol; 77 78 return true; 79 } 80 81 bool 82 ip_set_get_ip4_port(const struct sk_buff *skb, bool src, 83 __be16 *port, u8 *proto) 84 { 85 const struct iphdr *iph = ip_hdr(skb); 86 unsigned int protooff = ip_hdrlen(skb); 87 int protocol = iph->protocol; 88 89 /* See comments at tcp_match in ip_tables.c */ 90 if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) 91 return false; 92 93 return get_port(skb, protocol, protooff, src, port, proto); 94 } 95 EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); 96 97 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) 98 bool 99 ip_set_get_ip6_port(const struct sk_buff *skb, bool src, 100 __be16 *port, u8 *proto) 101 { 102 int protoff; 103 u8 nexthdr; 104 105 nexthdr = ipv6_hdr(skb)->nexthdr; 106 protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); 107 if (protoff < 0) 108 return false; 109 110 return get_port(skb, nexthdr, protoff, src, port, proto); 111 } 112 EXPORT_SYMBOL_GPL(ip_set_get_ip6_port); 113 #endif 114 115 bool 116 ip_set_get_ip_port(const struct sk_buff *skb, u8 pf, bool src, __be16 *port) 117 { 118 bool ret; 119 u8 proto; 120 121 switch (pf) { 122 case AF_INET: 123 ret = ip_set_get_ip4_port(skb, src, port, &proto); 124 break; 125 case AF_INET6: 126 ret = ip_set_get_ip6_port(skb, src, port, &proto); 127 break; 128 default: 129 return false; 130 } 131 if (!ret) 132 return ret; 133 switch (proto) { 134 case IPPROTO_TCP: 135 case IPPROTO_UDP: 136 return true; 137 default: 138 return false; 139 } 140 } 141 EXPORT_SYMBOL_GPL(ip_set_get_ip_port); 142