1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2007-2008 BalaBit IT Ltd. 4 * Author: Krisztian Kovacs 5 */ 6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 #include <linux/module.h> 8 #include <linux/skbuff.h> 9 #include <net/tcp.h> 10 #include <net/udp.h> 11 #include <net/icmp.h> 12 #include <net/sock.h> 13 #include <net/inet_sock.h> 14 #include <net/netfilter/nf_socket.h> 15 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 16 #include <net/netfilter/nf_conntrack.h> 17 #endif 18 19 static int 20 extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, 21 __be32 *raddr, __be32 *laddr, 22 __be16 *rport, __be16 *lport) 23 { 24 unsigned int outside_hdrlen = ip_hdrlen(skb); 25 struct iphdr *inside_iph, _inside_iph; 26 struct icmphdr *icmph, _icmph; 27 __be16 *ports, _ports[2]; 28 29 icmph = skb_header_pointer(skb, outside_hdrlen, 30 sizeof(_icmph), &_icmph); 31 if (icmph == NULL) 32 return 1; 33 34 if (!icmp_is_err(icmph->type)) 35 return 1; 36 37 inside_iph = skb_header_pointer(skb, outside_hdrlen + 38 sizeof(struct icmphdr), 39 sizeof(_inside_iph), &_inside_iph); 40 if (inside_iph == NULL) 41 return 1; 42 43 if (inside_iph->protocol != IPPROTO_TCP && 44 inside_iph->protocol != IPPROTO_UDP) 45 return 1; 46 47 ports = skb_header_pointer(skb, outside_hdrlen + 48 sizeof(struct icmphdr) + 49 (inside_iph->ihl << 2), 50 sizeof(_ports), &_ports); 51 if (ports == NULL) 52 return 1; 53 54 /* the inside IP packet is the one quoted from our side, thus 55 * its saddr is the local address */ 56 *protocol = inside_iph->protocol; 57 *laddr = inside_iph->saddr; 58 *lport = ports[0]; 59 *raddr = inside_iph->daddr; 60 *rport = ports[1]; 61 62 return 0; 63 } 64 65 static struct sock * 66 nf_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff, 67 const u8 protocol, 68 const __be32 saddr, const __be32 daddr, 69 const __be16 sport, const __be16 dport, 70 const struct net_device *in) 71 { 72 switch (protocol) { 73 case IPPROTO_TCP: 74 return inet_lookup(net, &tcp_hashinfo, skb, doff, 75 saddr, sport, daddr, dport, 76 in->ifindex); 77 case IPPROTO_UDP: 78 return udp4_lib_lookup(net, saddr, sport, daddr, dport, 79 in->ifindex); 80 } 81 return NULL; 82 } 83 84 struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, 85 const struct net_device *indev) 86 { 87 __be32 daddr, saddr; 88 __be16 dport, sport; 89 const struct iphdr *iph = ip_hdr(skb); 90 struct sk_buff *data_skb = NULL; 91 u8 protocol; 92 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 93 enum ip_conntrack_info ctinfo; 94 struct nf_conn const *ct; 95 #endif 96 int doff = 0; 97 98 if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { 99 struct tcphdr _hdr; 100 struct udphdr *hp; 101 102 hp = skb_header_pointer(skb, ip_hdrlen(skb), 103 iph->protocol == IPPROTO_UDP ? 104 sizeof(*hp) : sizeof(_hdr), &_hdr); 105 if (hp == NULL) 106 return NULL; 107 108 protocol = iph->protocol; 109 saddr = iph->saddr; 110 sport = hp->source; 111 daddr = iph->daddr; 112 dport = hp->dest; 113 data_skb = (struct sk_buff *)skb; 114 doff = iph->protocol == IPPROTO_TCP ? 115 ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) : 116 ip_hdrlen(skb) + sizeof(*hp); 117 118 } else if (iph->protocol == IPPROTO_ICMP) { 119 if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, 120 &sport, &dport)) 121 return NULL; 122 } else { 123 return NULL; 124 } 125 126 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 127 /* Do the lookup with the original socket address in 128 * case this is a reply packet of an established 129 * SNAT-ted connection. 130 */ 131 ct = nf_ct_get(skb, &ctinfo); 132 if (ct && 133 ((iph->protocol != IPPROTO_ICMP && 134 ctinfo == IP_CT_ESTABLISHED_REPLY) || 135 (iph->protocol == IPPROTO_ICMP && 136 ctinfo == IP_CT_RELATED_REPLY)) && 137 (ct->status & IPS_SRC_NAT_DONE)) { 138 139 daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; 140 dport = (iph->protocol == IPPROTO_TCP) ? 141 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : 142 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; 143 } 144 #endif 145 146 return nf_socket_get_sock_v4(net, data_skb, doff, protocol, saddr, 147 daddr, sport, dport, indev); 148 } 149 EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v4); 150 151 MODULE_LICENSE("GPL"); 152 MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 153 MODULE_DESCRIPTION("Netfilter IPv4 socket lookup infrastructure"); 154