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