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