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/inet6_hashtables.h> |
15 | #include <net/netfilter/nf_socket.h> |
16 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
17 | #include <net/netfilter/nf_conntrack.h> |
18 | #endif |
19 | |
20 | static int |
21 | (const struct sk_buff *skb, |
22 | unsigned int outside_hdrlen, |
23 | int *protocol, |
24 | const struct in6_addr **raddr, |
25 | const struct in6_addr **laddr, |
26 | __be16 *rport, |
27 | __be16 *lport, |
28 | struct ipv6hdr *ipv6_var) |
29 | { |
30 | const struct ipv6hdr *inside_iph; |
31 | struct icmp6hdr *icmph, _icmph; |
32 | __be16 *ports, _ports[2]; |
33 | u8 inside_nexthdr; |
34 | __be16 inside_fragoff; |
35 | int inside_hdrlen; |
36 | |
37 | icmph = skb_header_pointer(skb, offset: outside_hdrlen, |
38 | len: sizeof(_icmph), buffer: &_icmph); |
39 | if (icmph == NULL) |
40 | return 1; |
41 | |
42 | if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) |
43 | return 1; |
44 | |
45 | inside_iph = skb_header_pointer(skb, offset: outside_hdrlen + sizeof(_icmph), |
46 | len: sizeof(*ipv6_var), buffer: ipv6_var); |
47 | if (inside_iph == NULL) |
48 | return 1; |
49 | inside_nexthdr = inside_iph->nexthdr; |
50 | |
51 | inside_hdrlen = ipv6_skip_exthdr(skb, start: outside_hdrlen + sizeof(_icmph) + |
52 | sizeof(*ipv6_var), |
53 | nexthdrp: &inside_nexthdr, frag_offp: &inside_fragoff); |
54 | if (inside_hdrlen < 0) |
55 | return 1; /* hjm: Packet has no/incomplete transport layer headers. */ |
56 | |
57 | if (inside_nexthdr != IPPROTO_TCP && |
58 | inside_nexthdr != IPPROTO_UDP) |
59 | return 1; |
60 | |
61 | ports = skb_header_pointer(skb, offset: inside_hdrlen, |
62 | len: sizeof(_ports), buffer: &_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_nexthdr; |
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_v6(struct net *net, struct sk_buff *skb, int doff, |
79 | const u8 protocol, |
80 | const struct in6_addr *saddr, const struct in6_addr *daddr, |
81 | const __be16 sport, const __be16 dport, |
82 | const struct net_device *in) |
83 | { |
84 | switch (protocol) { |
85 | case IPPROTO_TCP: |
86 | return inet6_lookup(net, hashinfo: net->ipv4.tcp_death_row.hashinfo, |
87 | skb, doff, saddr, sport, daddr, dport, |
88 | dif: in->ifindex); |
89 | case IPPROTO_UDP: |
90 | return udp6_lib_lookup(net, saddr, sport, daddr, dport, |
91 | dif: in->ifindex); |
92 | } |
93 | |
94 | return NULL; |
95 | } |
96 | |
97 | struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb, |
98 | const struct net_device *indev) |
99 | { |
100 | __be16 dport, sport; |
101 | const struct in6_addr *daddr = NULL, *saddr = NULL; |
102 | struct ipv6hdr *iph = ipv6_hdr(skb), ipv6_var; |
103 | struct sk_buff *data_skb = NULL; |
104 | int doff = 0; |
105 | int thoff = 0, tproto; |
106 | |
107 | tproto = ipv6_find_hdr(skb, offset: &thoff, target: -1, NULL, NULL); |
108 | if (tproto < 0) { |
109 | pr_debug("unable to find transport header in IPv6 packet, dropping\n" ); |
110 | return NULL; |
111 | } |
112 | |
113 | if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { |
114 | struct tcphdr _hdr; |
115 | struct udphdr *hp; |
116 | |
117 | hp = skb_header_pointer(skb, offset: thoff, len: tproto == IPPROTO_UDP ? |
118 | sizeof(*hp) : sizeof(_hdr), buffer: &_hdr); |
119 | if (hp == NULL) |
120 | return NULL; |
121 | |
122 | saddr = &iph->saddr; |
123 | sport = hp->source; |
124 | daddr = &iph->daddr; |
125 | dport = hp->dest; |
126 | data_skb = (struct sk_buff *)skb; |
127 | doff = tproto == IPPROTO_TCP ? |
128 | thoff + __tcp_hdrlen(th: (struct tcphdr *)hp) : |
129 | thoff + sizeof(*hp); |
130 | |
131 | } else if (tproto == IPPROTO_ICMPV6) { |
132 | if (extract_icmp6_fields(skb, outside_hdrlen: thoff, protocol: &tproto, raddr: &saddr, laddr: &daddr, |
133 | rport: &sport, lport: &dport, ipv6_var: &ipv6_var)) |
134 | return NULL; |
135 | } else { |
136 | return NULL; |
137 | } |
138 | |
139 | return nf_socket_get_sock_v6(net, skb: data_skb, doff, protocol: tproto, saddr, daddr, |
140 | sport, dport, in: indev); |
141 | } |
142 | EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v6); |
143 | |
144 | MODULE_LICENSE("GPL" ); |
145 | MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler" ); |
146 | MODULE_DESCRIPTION("Netfilter IPv6 socket lookup infrastructure" ); |
147 | |