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 | (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, offset: outside_hdrlen, |
30 | len: sizeof(_icmph), buffer: &_icmph); |
31 | if (icmph == NULL) |
32 | return 1; |
33 | |
34 | if (!icmp_is_err(type: icmph->type)) |
35 | return 1; |
36 | |
37 | inside_iph = skb_header_pointer(skb, offset: outside_hdrlen + |
38 | sizeof(struct icmphdr), |
39 | len: sizeof(_inside_iph), buffer: &_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, offset: outside_hdrlen + |
48 | sizeof(struct icmphdr) + |
49 | (inside_iph->ihl << 2), |
50 | len: sizeof(_ports), buffer: &_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, hashinfo: net->ipv4.tcp_death_row.hashinfo, |
75 | skb, doff, saddr, sport, daddr, dport, |
76 | dif: in->ifindex); |
77 | case IPPROTO_UDP: |
78 | return udp4_lib_lookup(net, saddr, sport, daddr, dport, |
79 | dif: 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, offset: ip_hdrlen(skb), |
103 | len: iph->protocol == IPPROTO_UDP ? |
104 | sizeof(*hp) : sizeof(_hdr), buffer: &_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(th: (struct tcphdr *)hp) : |
116 | ip_hdrlen(skb) + sizeof(*hp); |
117 | |
118 | } else if (iph->protocol == IPPROTO_ICMP) { |
119 | if (extract_icmp4_fields(skb, protocol: &protocol, raddr: &saddr, laddr: &daddr, |
120 | rport: &sport, lport: &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: &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, skb: data_skb, doff, protocol, saddr, |
147 | daddr, sport, dport, in: 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 | |