diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2013-05-22 16:17:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-05-26 00:07:49 -0400 |
commit | 6d0bfe22611602f36617bc7aa2ffa1bbb2f54c67 (patch) | |
tree | 140f06221a5da5c44526cb61f921d80fec2132c2 | |
parent | 6e2842f4bbb5abcf57d3cb0f10870e0ce20c0ba2 (diff) |
net: ipv6: Add IPv6 support to the ping socket.
This adds the ability to send ICMPv6 echo requests without a
raw socket. The equivalent ability for ICMPv4 was added in
2011.
Instead of having separate code paths for IPv4 and IPv6, make
most of the code in net/ipv4/ping.c dual-stack and only add a
few IPv6-specific bits (like the protocol definition) to a new
net/ipv6/ping.c. Hopefully this will reduce divergence and/or
duplication of bugs in the future.
Caveats:
- Setting options via ancillary data (e.g., using IPV6_PKTINFO
to specify the outgoing interface) is not yet supported.
- There are no separate security settings for IPv4 and IPv6;
everything is controlled by /proc/net/ipv4/ping_group_range.
- The proc interface does not yet display IPv6 ping sockets
properly.
Tested with a patched copy of ping6 and using raw socket calls.
Compiles and works with all of CONFIG_IPV6={n,m,y}.
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ipv6.h | 6 | ||||
-rw-r--r-- | include/net/ping.h | 49 | ||||
-rw-r--r-- | include/net/transp_v6.h | 3 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 5 | ||||
-rw-r--r-- | net/ipv4/ping.c | 557 | ||||
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 12 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 19 | ||||
-rw-r--r-- | net/ipv6/ping.c | 216 |
9 files changed, 698 insertions, 171 deletions
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 0810aa57c780..ab47582f6c0b 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -260,6 +260,12 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl) | |||
260 | 260 | ||
261 | extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); | 261 | extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); |
262 | 262 | ||
263 | int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, | ||
264 | struct icmp6hdr *thdr, int len); | ||
265 | |||
266 | struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, | ||
267 | struct sock *sk, struct flowi6 *fl6); | ||
268 | |||
263 | extern int ip6_ra_control(struct sock *sk, int sel); | 269 | extern int ip6_ra_control(struct sock *sk, int sel); |
264 | 270 | ||
265 | extern int ipv6_parse_hopopts(struct sk_buff *skb); | 271 | extern int ipv6_parse_hopopts(struct sk_buff *skb); |
diff --git a/include/net/ping.h b/include/net/ping.h index 682b5ae9af51..9242fa090d3d 100644 --- a/include/net/ping.h +++ b/include/net/ping.h | |||
@@ -13,6 +13,7 @@ | |||
13 | #ifndef _PING_H | 13 | #ifndef _PING_H |
14 | #define _PING_H | 14 | #define _PING_H |
15 | 15 | ||
16 | #include <net/icmp.h> | ||
16 | #include <net/netns/hash.h> | 17 | #include <net/netns/hash.h> |
17 | 18 | ||
18 | /* PING_HTABLE_SIZE must be power of 2 */ | 19 | /* PING_HTABLE_SIZE must be power of 2 */ |
@@ -28,6 +29,18 @@ | |||
28 | */ | 29 | */ |
29 | #define GID_T_MAX (((gid_t)~0U) >> 1) | 30 | #define GID_T_MAX (((gid_t)~0U) >> 1) |
30 | 31 | ||
32 | /* Compatibility glue so we can support IPv6 when it's compiled as a module */ | ||
33 | struct pingv6_ops { | ||
34 | int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len); | ||
35 | int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg, | ||
36 | struct sk_buff *skb); | ||
37 | int (*icmpv6_err_convert)(u8 type, u8 code, int *err); | ||
38 | void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err, | ||
39 | __be16 port, u32 info, u8 *payload); | ||
40 | int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr, | ||
41 | struct net_device *dev, int strict); | ||
42 | }; | ||
43 | |||
31 | struct ping_table { | 44 | struct ping_table { |
32 | struct hlist_nulls_head hash[PING_HTABLE_SIZE]; | 45 | struct hlist_nulls_head hash[PING_HTABLE_SIZE]; |
33 | rwlock_t lock; | 46 | rwlock_t lock; |
@@ -39,10 +52,39 @@ struct ping_iter_state { | |||
39 | }; | 52 | }; |
40 | 53 | ||
41 | extern struct proto ping_prot; | 54 | extern struct proto ping_prot; |
55 | extern struct ping_table ping_table; | ||
56 | #if IS_ENABLED(CONFIG_IPV6) | ||
57 | extern struct pingv6_ops pingv6_ops; | ||
58 | #endif | ||
42 | 59 | ||
60 | struct pingfakehdr { | ||
61 | struct icmphdr icmph; | ||
62 | struct iovec *iov; | ||
63 | sa_family_t family; | ||
64 | __wsum wcheck; | ||
65 | }; | ||
43 | 66 | ||
44 | extern void ping_rcv(struct sk_buff *); | 67 | int ping_get_port(struct sock *sk, unsigned short ident); |
45 | extern void ping_err(struct sk_buff *, u32 info); | 68 | void ping_hash(struct sock *sk); |
69 | void ping_unhash(struct sock *sk); | ||
70 | |||
71 | int ping_init_sock(struct sock *sk); | ||
72 | void ping_close(struct sock *sk, long timeout); | ||
73 | int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len); | ||
74 | void ping_err(struct sk_buff *skb, int offset, u32 info); | ||
75 | int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd, | ||
76 | struct sk_buff *); | ||
77 | |||
78 | int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
79 | size_t len, int noblock, int flags, int *addr_len); | ||
80 | int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, | ||
81 | void *user_icmph, size_t icmph_len); | ||
82 | int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
83 | size_t len); | ||
84 | int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
85 | size_t len); | ||
86 | int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); | ||
87 | void ping_rcv(struct sk_buff *skb); | ||
46 | 88 | ||
47 | #ifdef CONFIG_PROC_FS | 89 | #ifdef CONFIG_PROC_FS |
48 | extern int __init ping_proc_init(void); | 90 | extern int __init ping_proc_init(void); |
@@ -50,6 +92,7 @@ extern void ping_proc_exit(void); | |||
50 | #endif | 92 | #endif |
51 | 93 | ||
52 | void __init ping_init(void); | 94 | void __init ping_init(void); |
53 | 95 | int __init pingv6_init(void); | |
96 | void pingv6_exit(void); | ||
54 | 97 | ||
55 | #endif /* _PING_H */ | 98 | #endif /* _PING_H */ |
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 938b7fd11204..eb40e71ff2ee 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h | |||
@@ -11,6 +11,7 @@ extern struct proto rawv6_prot; | |||
11 | extern struct proto udpv6_prot; | 11 | extern struct proto udpv6_prot; |
12 | extern struct proto udplitev6_prot; | 12 | extern struct proto udplitev6_prot; |
13 | extern struct proto tcpv6_prot; | 13 | extern struct proto tcpv6_prot; |
14 | extern struct proto pingv6_prot; | ||
14 | 15 | ||
15 | struct flowi6; | 16 | struct flowi6; |
16 | 17 | ||
@@ -21,6 +22,8 @@ extern int ipv6_frag_init(void); | |||
21 | extern void ipv6_frag_exit(void); | 22 | extern void ipv6_frag_exit(void); |
22 | 23 | ||
23 | /* transport protocols */ | 24 | /* transport protocols */ |
25 | extern int pingv6_init(void); | ||
26 | extern void pingv6_exit(void); | ||
24 | extern int rawv6_init(void); | 27 | extern int rawv6_init(void); |
25 | extern void rawv6_exit(void); | 28 | extern void rawv6_exit(void); |
26 | extern int udpv6_init(void); | 29 | extern int udpv6_init(void); |
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 76e10b47e053..562efd91f457 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -939,7 +939,8 @@ error: | |||
939 | void icmp_err(struct sk_buff *skb, u32 info) | 939 | void icmp_err(struct sk_buff *skb, u32 info) |
940 | { | 940 | { |
941 | struct iphdr *iph = (struct iphdr *)skb->data; | 941 | struct iphdr *iph = (struct iphdr *)skb->data; |
942 | struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); | 942 | int offset = iph->ihl<<2; |
943 | struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset); | ||
943 | int type = icmp_hdr(skb)->type; | 944 | int type = icmp_hdr(skb)->type; |
944 | int code = icmp_hdr(skb)->code; | 945 | int code = icmp_hdr(skb)->code; |
945 | struct net *net = dev_net(skb->dev); | 946 | struct net *net = dev_net(skb->dev); |
@@ -949,7 +950,7 @@ void icmp_err(struct sk_buff *skb, u32 info) | |||
949 | * triggered by ICMP_ECHOREPLY which sent from kernel. | 950 | * triggered by ICMP_ECHOREPLY which sent from kernel. |
950 | */ | 951 | */ |
951 | if (icmph->type != ICMP_ECHOREPLY) { | 952 | if (icmph->type != ICMP_ECHOREPLY) { |
952 | ping_err(skb, info); | 953 | ping_err(skb, offset, info); |
953 | return; | 954 | return; |
954 | } | 955 | } |
955 | 956 | ||
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 7d93d62cd5fd..71f6ad02fa67 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c | |||
@@ -33,7 +33,6 @@ | |||
33 | #include <linux/netdevice.h> | 33 | #include <linux/netdevice.h> |
34 | #include <net/snmp.h> | 34 | #include <net/snmp.h> |
35 | #include <net/ip.h> | 35 | #include <net/ip.h> |
36 | #include <net/ipv6.h> | ||
37 | #include <net/icmp.h> | 36 | #include <net/icmp.h> |
38 | #include <net/protocol.h> | 37 | #include <net/protocol.h> |
39 | #include <linux/skbuff.h> | 38 | #include <linux/skbuff.h> |
@@ -46,8 +45,18 @@ | |||
46 | #include <net/inet_common.h> | 45 | #include <net/inet_common.h> |
47 | #include <net/checksum.h> | 46 | #include <net/checksum.h> |
48 | 47 | ||
48 | #if IS_ENABLED(CONFIG_IPV6) | ||
49 | #include <linux/in6.h> | ||
50 | #include <linux/icmpv6.h> | ||
51 | #include <net/addrconf.h> | ||
52 | #include <net/ipv6.h> | ||
53 | #include <net/transp_v6.h> | ||
54 | #endif | ||
49 | 55 | ||
50 | static struct ping_table ping_table; | 56 | |
57 | struct ping_table ping_table; | ||
58 | struct pingv6_ops pingv6_ops; | ||
59 | EXPORT_SYMBOL_GPL(pingv6_ops); | ||
51 | 60 | ||
52 | static u16 ping_port_rover; | 61 | static u16 ping_port_rover; |
53 | 62 | ||
@@ -58,6 +67,7 @@ static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int ma | |||
58 | pr_debug("hash(%d) = %d\n", num, res); | 67 | pr_debug("hash(%d) = %d\n", num, res); |
59 | return res; | 68 | return res; |
60 | } | 69 | } |
70 | EXPORT_SYMBOL_GPL(ping_hash); | ||
61 | 71 | ||
62 | static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, | 72 | static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, |
63 | struct net *net, unsigned int num) | 73 | struct net *net, unsigned int num) |
@@ -65,7 +75,7 @@ static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, | |||
65 | return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; | 75 | return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; |
66 | } | 76 | } |
67 | 77 | ||
68 | static int ping_v4_get_port(struct sock *sk, unsigned short ident) | 78 | int ping_get_port(struct sock *sk, unsigned short ident) |
69 | { | 79 | { |
70 | struct hlist_nulls_node *node; | 80 | struct hlist_nulls_node *node; |
71 | struct hlist_nulls_head *hlist; | 81 | struct hlist_nulls_head *hlist; |
@@ -103,6 +113,10 @@ next_port: | |||
103 | ping_portaddr_for_each_entry(sk2, node, hlist) { | 113 | ping_portaddr_for_each_entry(sk2, node, hlist) { |
104 | isk2 = inet_sk(sk2); | 114 | isk2 = inet_sk(sk2); |
105 | 115 | ||
116 | /* BUG? Why is this reuse and not reuseaddr? ping.c | ||
117 | * doesn't turn off SO_REUSEADDR, and it doesn't expect | ||
118 | * that other ping processes can steal its packets. | ||
119 | */ | ||
106 | if ((isk2->inet_num == ident) && | 120 | if ((isk2->inet_num == ident) && |
107 | (sk2 != sk) && | 121 | (sk2 != sk) && |
108 | (!sk2->sk_reuse || !sk->sk_reuse)) | 122 | (!sk2->sk_reuse || !sk->sk_reuse)) |
@@ -125,17 +139,18 @@ fail: | |||
125 | write_unlock_bh(&ping_table.lock); | 139 | write_unlock_bh(&ping_table.lock); |
126 | return 1; | 140 | return 1; |
127 | } | 141 | } |
142 | EXPORT_SYMBOL_GPL(ping_get_port); | ||
128 | 143 | ||
129 | static void ping_v4_hash(struct sock *sk) | 144 | void ping_hash(struct sock *sk) |
130 | { | 145 | { |
131 | pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); | 146 | pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); |
132 | BUG(); /* "Please do not press this button again." */ | 147 | BUG(); /* "Please do not press this button again." */ |
133 | } | 148 | } |
134 | 149 | ||
135 | static void ping_v4_unhash(struct sock *sk) | 150 | void ping_unhash(struct sock *sk) |
136 | { | 151 | { |
137 | struct inet_sock *isk = inet_sk(sk); | 152 | struct inet_sock *isk = inet_sk(sk); |
138 | pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); | 153 | pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); |
139 | if (sk_hashed(sk)) { | 154 | if (sk_hashed(sk)) { |
140 | write_lock_bh(&ping_table.lock); | 155 | write_lock_bh(&ping_table.lock); |
141 | hlist_nulls_del(&sk->sk_nulls_node); | 156 | hlist_nulls_del(&sk->sk_nulls_node); |
@@ -146,31 +161,61 @@ static void ping_v4_unhash(struct sock *sk) | |||
146 | write_unlock_bh(&ping_table.lock); | 161 | write_unlock_bh(&ping_table.lock); |
147 | } | 162 | } |
148 | } | 163 | } |
164 | EXPORT_SYMBOL_GPL(ping_unhash); | ||
149 | 165 | ||
150 | static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr, | 166 | static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) |
151 | u16 ident, int dif) | ||
152 | { | 167 | { |
153 | struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); | 168 | struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); |
154 | struct sock *sk = NULL; | 169 | struct sock *sk = NULL; |
155 | struct inet_sock *isk; | 170 | struct inet_sock *isk; |
156 | struct hlist_nulls_node *hnode; | 171 | struct hlist_nulls_node *hnode; |
172 | int dif = skb->dev->ifindex; | ||
173 | |||
174 | if (skb->protocol == htons(ETH_P_IP)) { | ||
175 | pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", | ||
176 | (int)ident, &ip_hdr(skb)->daddr, dif); | ||
177 | #if IS_ENABLED(CONFIG_IPV6) | ||
178 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | ||
179 | pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n", | ||
180 | (int)ident, &ipv6_hdr(skb)->daddr, dif); | ||
181 | #endif | ||
182 | } | ||
157 | 183 | ||
158 | pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", | ||
159 | (int)ident, &daddr, dif); | ||
160 | read_lock_bh(&ping_table.lock); | 184 | read_lock_bh(&ping_table.lock); |
161 | 185 | ||
162 | ping_portaddr_for_each_entry(sk, hnode, hslot) { | 186 | ping_portaddr_for_each_entry(sk, hnode, hslot) { |
163 | isk = inet_sk(sk); | 187 | isk = inet_sk(sk); |
164 | 188 | ||
165 | pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk, | ||
166 | (int)isk->inet_num, &isk->inet_rcv_saddr, | ||
167 | sk->sk_bound_dev_if); | ||
168 | |||
169 | pr_debug("iterate\n"); | 189 | pr_debug("iterate\n"); |
170 | if (isk->inet_num != ident) | 190 | if (isk->inet_num != ident) |
171 | continue; | 191 | continue; |
172 | if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr) | 192 | |
173 | continue; | 193 | if (skb->protocol == htons(ETH_P_IP) && |
194 | sk->sk_family == AF_INET) { | ||
195 | pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, | ||
196 | (int) isk->inet_num, &isk->inet_rcv_saddr, | ||
197 | sk->sk_bound_dev_if); | ||
198 | |||
199 | if (isk->inet_rcv_saddr && | ||
200 | isk->inet_rcv_saddr != ip_hdr(skb)->daddr) | ||
201 | continue; | ||
202 | #if IS_ENABLED(CONFIG_IPV6) | ||
203 | } else if (skb->protocol == htons(ETH_P_IPV6) && | ||
204 | sk->sk_family == AF_INET6) { | ||
205 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
206 | |||
207 | pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, | ||
208 | (int) isk->inet_num, | ||
209 | &inet6_sk(sk)->rcv_saddr, | ||
210 | sk->sk_bound_dev_if); | ||
211 | |||
212 | if (!ipv6_addr_any(&np->rcv_saddr) && | ||
213 | !ipv6_addr_equal(&np->rcv_saddr, | ||
214 | &ipv6_hdr(skb)->daddr)) | ||
215 | continue; | ||
216 | #endif | ||
217 | } | ||
218 | |||
174 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) | 219 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) |
175 | continue; | 220 | continue; |
176 | 221 | ||
@@ -200,7 +245,7 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, | |||
200 | } | 245 | } |
201 | 246 | ||
202 | 247 | ||
203 | static int ping_init_sock(struct sock *sk) | 248 | int ping_init_sock(struct sock *sk) |
204 | { | 249 | { |
205 | struct net *net = sock_net(sk); | 250 | struct net *net = sock_net(sk); |
206 | kgid_t group = current_egid(); | 251 | kgid_t group = current_egid(); |
@@ -225,8 +270,9 @@ static int ping_init_sock(struct sock *sk) | |||
225 | 270 | ||
226 | return -EACCES; | 271 | return -EACCES; |
227 | } | 272 | } |
273 | EXPORT_SYMBOL_GPL(ping_init_sock); | ||
228 | 274 | ||
229 | static void ping_close(struct sock *sk, long timeout) | 275 | void ping_close(struct sock *sk, long timeout) |
230 | { | 276 | { |
231 | pr_debug("ping_close(sk=%p,sk->num=%u)\n", | 277 | pr_debug("ping_close(sk=%p,sk->num=%u)\n", |
232 | inet_sk(sk), inet_sk(sk)->inet_num); | 278 | inet_sk(sk), inet_sk(sk)->inet_num); |
@@ -234,36 +280,122 @@ static void ping_close(struct sock *sk, long timeout) | |||
234 | 280 | ||
235 | sk_common_release(sk); | 281 | sk_common_release(sk); |
236 | } | 282 | } |
283 | EXPORT_SYMBOL_GPL(ping_close); | ||
284 | |||
285 | /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ | ||
286 | int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, | ||
287 | struct sockaddr *uaddr, int addr_len) { | ||
288 | struct net *net = sock_net(sk); | ||
289 | if (sk->sk_family == AF_INET) { | ||
290 | struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; | ||
291 | int chk_addr_ret; | ||
292 | |||
293 | if (addr_len < sizeof(*addr)) | ||
294 | return -EINVAL; | ||
295 | |||
296 | pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", | ||
297 | sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); | ||
298 | |||
299 | chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr); | ||
300 | |||
301 | if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) | ||
302 | chk_addr_ret = RTN_LOCAL; | ||
303 | |||
304 | if ((sysctl_ip_nonlocal_bind == 0 && | ||
305 | isk->freebind == 0 && isk->transparent == 0 && | ||
306 | chk_addr_ret != RTN_LOCAL) || | ||
307 | chk_addr_ret == RTN_MULTICAST || | ||
308 | chk_addr_ret == RTN_BROADCAST) | ||
309 | return -EADDRNOTAVAIL; | ||
310 | |||
311 | #if IS_ENABLED(CONFIG_IPV6) | ||
312 | } else if (sk->sk_family == AF_INET6) { | ||
313 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; | ||
314 | int addr_type, scoped, has_addr; | ||
315 | struct net_device *dev = NULL; | ||
316 | |||
317 | if (addr_len < sizeof(*addr)) | ||
318 | return -EINVAL; | ||
319 | |||
320 | pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", | ||
321 | sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); | ||
322 | |||
323 | addr_type = ipv6_addr_type(&addr->sin6_addr); | ||
324 | scoped = __ipv6_addr_needs_scope_id(addr_type); | ||
325 | if ((addr_type != IPV6_ADDR_ANY && | ||
326 | !(addr_type & IPV6_ADDR_UNICAST)) || | ||
327 | (scoped && !addr->sin6_scope_id)) | ||
328 | return -EINVAL; | ||
329 | |||
330 | rcu_read_lock(); | ||
331 | if (addr->sin6_scope_id) { | ||
332 | dev = dev_get_by_index_rcu(net, addr->sin6_scope_id); | ||
333 | if (!dev) { | ||
334 | rcu_read_unlock(); | ||
335 | return -ENODEV; | ||
336 | } | ||
337 | } | ||
338 | has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, | ||
339 | scoped); | ||
340 | rcu_read_unlock(); | ||
341 | |||
342 | if (!(isk->freebind || isk->transparent || has_addr || | ||
343 | addr_type == IPV6_ADDR_ANY)) | ||
344 | return -EADDRNOTAVAIL; | ||
345 | |||
346 | if (scoped) | ||
347 | sk->sk_bound_dev_if = addr->sin6_scope_id; | ||
348 | #endif | ||
349 | } else { | ||
350 | return -EAFNOSUPPORT; | ||
351 | } | ||
352 | return 0; | ||
353 | } | ||
237 | 354 | ||
355 | void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) | ||
356 | { | ||
357 | if (saddr->sa_family == AF_INET) { | ||
358 | struct inet_sock *isk = inet_sk(sk); | ||
359 | struct sockaddr_in *addr = (struct sockaddr_in *) saddr; | ||
360 | isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; | ||
361 | #if IS_ENABLED(CONFIG_IPV6) | ||
362 | } else if (saddr->sa_family == AF_INET6) { | ||
363 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; | ||
364 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
365 | np->rcv_saddr = np->saddr = addr->sin6_addr; | ||
366 | #endif | ||
367 | } | ||
368 | } | ||
369 | |||
370 | void ping_clear_saddr(struct sock *sk, int dif) | ||
371 | { | ||
372 | sk->sk_bound_dev_if = dif; | ||
373 | if (sk->sk_family == AF_INET) { | ||
374 | struct inet_sock *isk = inet_sk(sk); | ||
375 | isk->inet_rcv_saddr = isk->inet_saddr = 0; | ||
376 | #if IS_ENABLED(CONFIG_IPV6) | ||
377 | } else if (sk->sk_family == AF_INET6) { | ||
378 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
379 | memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr)); | ||
380 | memset(&np->saddr, 0, sizeof(np->saddr)); | ||
381 | #endif | ||
382 | } | ||
383 | } | ||
238 | /* | 384 | /* |
239 | * We need our own bind because there are no privileged id's == local ports. | 385 | * We need our own bind because there are no privileged id's == local ports. |
240 | * Moreover, we don't allow binding to multi- and broadcast addresses. | 386 | * Moreover, we don't allow binding to multi- and broadcast addresses. |
241 | */ | 387 | */ |
242 | 388 | ||
243 | static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) | 389 | int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
244 | { | 390 | { |
245 | struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; | ||
246 | struct inet_sock *isk = inet_sk(sk); | 391 | struct inet_sock *isk = inet_sk(sk); |
247 | unsigned short snum; | 392 | unsigned short snum; |
248 | int chk_addr_ret; | ||
249 | int err; | 393 | int err; |
394 | int dif = sk->sk_bound_dev_if; | ||
250 | 395 | ||
251 | if (addr_len < sizeof(struct sockaddr_in)) | 396 | err = ping_check_bind_addr(sk, isk, uaddr, addr_len); |
252 | return -EINVAL; | 397 | if (err) |
253 | 398 | return err; | |
254 | pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n", | ||
255 | sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); | ||
256 | |||
257 | chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); | ||
258 | if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) | ||
259 | chk_addr_ret = RTN_LOCAL; | ||
260 | |||
261 | if ((sysctl_ip_nonlocal_bind == 0 && | ||
262 | isk->freebind == 0 && isk->transparent == 0 && | ||
263 | chk_addr_ret != RTN_LOCAL) || | ||
264 | chk_addr_ret == RTN_MULTICAST || | ||
265 | chk_addr_ret == RTN_BROADCAST) | ||
266 | return -EADDRNOTAVAIL; | ||
267 | 399 | ||
268 | lock_sock(sk); | 400 | lock_sock(sk); |
269 | 401 | ||
@@ -272,42 +404,50 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
272 | goto out; | 404 | goto out; |
273 | 405 | ||
274 | err = -EADDRINUSE; | 406 | err = -EADDRINUSE; |
275 | isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; | 407 | ping_set_saddr(sk, uaddr); |
276 | snum = ntohs(addr->sin_port); | 408 | snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); |
277 | if (ping_v4_get_port(sk, snum) != 0) { | 409 | if (ping_get_port(sk, snum) != 0) { |
278 | isk->inet_saddr = isk->inet_rcv_saddr = 0; | 410 | ping_clear_saddr(sk, dif); |
279 | goto out; | 411 | goto out; |
280 | } | 412 | } |
281 | 413 | ||
282 | pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n", | 414 | pr_debug("after bind(): num = %d, dif = %d\n", |
283 | (int)isk->inet_num, | 415 | (int)isk->inet_num, |
284 | &isk->inet_rcv_saddr, | ||
285 | (int)sk->sk_bound_dev_if); | 416 | (int)sk->sk_bound_dev_if); |
286 | 417 | ||
287 | err = 0; | 418 | err = 0; |
288 | if (isk->inet_rcv_saddr) | 419 | if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) || |
420 | (sk->sk_family == AF_INET6 && | ||
421 | !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr))) | ||
289 | sk->sk_userlocks |= SOCK_BINDADDR_LOCK; | 422 | sk->sk_userlocks |= SOCK_BINDADDR_LOCK; |
423 | |||
290 | if (snum) | 424 | if (snum) |
291 | sk->sk_userlocks |= SOCK_BINDPORT_LOCK; | 425 | sk->sk_userlocks |= SOCK_BINDPORT_LOCK; |
292 | isk->inet_sport = htons(isk->inet_num); | 426 | isk->inet_sport = htons(isk->inet_num); |
293 | isk->inet_daddr = 0; | 427 | isk->inet_daddr = 0; |
294 | isk->inet_dport = 0; | 428 | isk->inet_dport = 0; |
429 | |||
430 | #if IS_ENABLED(CONFIG_IPV6) | ||
431 | if (sk->sk_family == AF_INET6) | ||
432 | memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr)); | ||
433 | #endif | ||
434 | |||
295 | sk_dst_reset(sk); | 435 | sk_dst_reset(sk); |
296 | out: | 436 | out: |
297 | release_sock(sk); | 437 | release_sock(sk); |
298 | pr_debug("ping_v4_bind -> %d\n", err); | 438 | pr_debug("ping_v4_bind -> %d\n", err); |
299 | return err; | 439 | return err; |
300 | } | 440 | } |
441 | EXPORT_SYMBOL_GPL(ping_bind); | ||
301 | 442 | ||
302 | /* | 443 | /* |
303 | * Is this a supported type of ICMP message? | 444 | * Is this a supported type of ICMP message? |
304 | */ | 445 | */ |
305 | 446 | ||
306 | static inline int ping_supported(int type, int code) | 447 | static inline int ping_supported(int family, int type, int code) |
307 | { | 448 | { |
308 | if (type == ICMP_ECHO && code == 0) | 449 | return (family == AF_INET && type == ICMP_ECHO && code == 0) || |
309 | return 1; | 450 | (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0); |
310 | return 0; | ||
311 | } | 451 | } |
312 | 452 | ||
313 | /* | 453 | /* |
@@ -315,30 +455,42 @@ static inline int ping_supported(int type, int code) | |||
315 | * sort of error condition. | 455 | * sort of error condition. |
316 | */ | 456 | */ |
317 | 457 | ||
318 | static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); | 458 | void ping_err(struct sk_buff *skb, int offset, u32 info) |
319 | |||
320 | void ping_err(struct sk_buff *skb, u32 info) | ||
321 | { | 459 | { |
322 | struct iphdr *iph = (struct iphdr *)skb->data; | 460 | int family; |
323 | struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); | 461 | struct icmphdr *icmph; |
324 | struct inet_sock *inet_sock; | 462 | struct inet_sock *inet_sock; |
325 | int type = icmp_hdr(skb)->type; | 463 | int type; |
326 | int code = icmp_hdr(skb)->code; | 464 | int code; |
327 | struct net *net = dev_net(skb->dev); | 465 | struct net *net = dev_net(skb->dev); |
328 | struct sock *sk; | 466 | struct sock *sk; |
329 | int harderr; | 467 | int harderr; |
330 | int err; | 468 | int err; |
331 | 469 | ||
470 | if (skb->protocol == htons(ETH_P_IP)) { | ||
471 | family = AF_INET; | ||
472 | type = icmp_hdr(skb)->type; | ||
473 | code = icmp_hdr(skb)->code; | ||
474 | icmph = (struct icmphdr *)(skb->data + offset); | ||
475 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | ||
476 | family = AF_INET6; | ||
477 | type = icmp6_hdr(skb)->icmp6_type; | ||
478 | code = icmp6_hdr(skb)->icmp6_code; | ||
479 | icmph = (struct icmphdr *) (skb->data + offset); | ||
480 | } else { | ||
481 | BUG(); | ||
482 | } | ||
483 | |||
332 | /* We assume the packet has already been checked by icmp_unreach */ | 484 | /* We assume the packet has already been checked by icmp_unreach */ |
333 | 485 | ||
334 | if (!ping_supported(icmph->type, icmph->code)) | 486 | if (!ping_supported(family, icmph->type, icmph->code)) |
335 | return; | 487 | return; |
336 | 488 | ||
337 | pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type, | 489 | pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", |
338 | code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); | 490 | skb->protocol, type, code, ntohs(icmph->un.echo.id), |
491 | ntohs(icmph->un.echo.sequence)); | ||
339 | 492 | ||
340 | sk = ping_v4_lookup(net, iph->daddr, iph->saddr, | 493 | sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); |
341 | ntohs(icmph->un.echo.id), skb->dev->ifindex); | ||
342 | if (sk == NULL) { | 494 | if (sk == NULL) { |
343 | pr_debug("no socket, dropping\n"); | 495 | pr_debug("no socket, dropping\n"); |
344 | return; /* No socket for error */ | 496 | return; /* No socket for error */ |
@@ -349,72 +501,83 @@ void ping_err(struct sk_buff *skb, u32 info) | |||
349 | harderr = 0; | 501 | harderr = 0; |
350 | inet_sock = inet_sk(sk); | 502 | inet_sock = inet_sk(sk); |
351 | 503 | ||
352 | switch (type) { | 504 | if (skb->protocol == htons(ETH_P_IP)) { |
353 | default: | 505 | switch (type) { |
354 | case ICMP_TIME_EXCEEDED: | 506 | default: |
355 | err = EHOSTUNREACH; | 507 | case ICMP_TIME_EXCEEDED: |
356 | break; | 508 | err = EHOSTUNREACH; |
357 | case ICMP_SOURCE_QUENCH: | 509 | break; |
358 | /* This is not a real error but ping wants to see it. | 510 | case ICMP_SOURCE_QUENCH: |
359 | * Report it with some fake errno. */ | 511 | /* This is not a real error but ping wants to see it. |
360 | err = EREMOTEIO; | 512 | * Report it with some fake errno. |
361 | break; | 513 | */ |
362 | case ICMP_PARAMETERPROB: | 514 | err = EREMOTEIO; |
363 | err = EPROTO; | 515 | break; |
364 | harderr = 1; | 516 | case ICMP_PARAMETERPROB: |
365 | break; | 517 | err = EPROTO; |
366 | case ICMP_DEST_UNREACH: | 518 | harderr = 1; |
367 | if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ | 519 | break; |
368 | ipv4_sk_update_pmtu(skb, sk, info); | 520 | case ICMP_DEST_UNREACH: |
369 | if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { | 521 | if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ |
370 | err = EMSGSIZE; | 522 | ipv4_sk_update_pmtu(skb, sk, info); |
371 | harderr = 1; | 523 | if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { |
372 | break; | 524 | err = EMSGSIZE; |
525 | harderr = 1; | ||
526 | break; | ||
527 | } | ||
528 | goto out; | ||
373 | } | 529 | } |
374 | goto out; | 530 | err = EHOSTUNREACH; |
375 | } | 531 | if (code <= NR_ICMP_UNREACH) { |
376 | err = EHOSTUNREACH; | 532 | harderr = icmp_err_convert[code].fatal; |
377 | if (code <= NR_ICMP_UNREACH) { | 533 | err = icmp_err_convert[code].errno; |
378 | harderr = icmp_err_convert[code].fatal; | 534 | } |
379 | err = icmp_err_convert[code].errno; | 535 | break; |
536 | case ICMP_REDIRECT: | ||
537 | /* See ICMP_SOURCE_QUENCH */ | ||
538 | ipv4_sk_redirect(skb, sk); | ||
539 | err = EREMOTEIO; | ||
540 | break; | ||
380 | } | 541 | } |
381 | break; | 542 | #if IS_ENABLED(CONFIG_IPV6) |
382 | case ICMP_REDIRECT: | 543 | } else if (skb->protocol == htons(ETH_P_IPV6)) { |
383 | /* See ICMP_SOURCE_QUENCH */ | 544 | harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); |
384 | ipv4_sk_redirect(skb, sk); | 545 | #endif |
385 | err = EREMOTEIO; | ||
386 | break; | ||
387 | } | 546 | } |
388 | 547 | ||
389 | /* | 548 | /* |
390 | * RFC1122: OK. Passes ICMP errors back to application, as per | 549 | * RFC1122: OK. Passes ICMP errors back to application, as per |
391 | * 4.1.3.3. | 550 | * 4.1.3.3. |
392 | */ | 551 | */ |
393 | if (!inet_sock->recverr) { | 552 | if ((family == AF_INET && !inet_sock->recverr) || |
553 | (family == AF_INET6 && !inet6_sk(sk)->recverr)) { | ||
394 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) | 554 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) |
395 | goto out; | 555 | goto out; |
396 | } else { | 556 | } else { |
397 | ip_icmp_error(sk, skb, err, 0 /* no remote port */, | 557 | if (family == AF_INET) { |
398 | info, (u8 *)icmph); | 558 | ip_icmp_error(sk, skb, err, 0 /* no remote port */, |
559 | info, (u8 *)icmph); | ||
560 | #if IS_ENABLED(CONFIG_IPV6) | ||
561 | } else if (family == AF_INET6) { | ||
562 | pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, | ||
563 | info, (u8 *)icmph); | ||
564 | #endif | ||
565 | } | ||
399 | } | 566 | } |
400 | sk->sk_err = err; | 567 | sk->sk_err = err; |
401 | sk->sk_error_report(sk); | 568 | sk->sk_error_report(sk); |
402 | out: | 569 | out: |
403 | sock_put(sk); | 570 | sock_put(sk); |
404 | } | 571 | } |
572 | EXPORT_SYMBOL_GPL(ping_err); | ||
405 | 573 | ||
406 | /* | 574 | /* |
407 | * Copy and checksum an ICMP Echo packet from user space into a buffer. | 575 | * Copy and checksum an ICMP Echo packet from user space into a buffer |
576 | * starting from the payload. | ||
408 | */ | 577 | */ |
409 | 578 | ||
410 | struct pingfakehdr { | 579 | int ping_getfrag(void *from, char *to, |
411 | struct icmphdr icmph; | 580 | int offset, int fraglen, int odd, struct sk_buff *skb) |
412 | struct iovec *iov; | ||
413 | __wsum wcheck; | ||
414 | }; | ||
415 | |||
416 | static int ping_getfrag(void *from, char *to, | ||
417 | int offset, int fraglen, int odd, struct sk_buff *skb) | ||
418 | { | 581 | { |
419 | struct pingfakehdr *pfh = (struct pingfakehdr *)from; | 582 | struct pingfakehdr *pfh = (struct pingfakehdr *)from; |
420 | 583 | ||
@@ -425,20 +588,33 @@ static int ping_getfrag(void *from, char *to, | |||
425 | pfh->iov, 0, fraglen - sizeof(struct icmphdr), | 588 | pfh->iov, 0, fraglen - sizeof(struct icmphdr), |
426 | &pfh->wcheck)) | 589 | &pfh->wcheck)) |
427 | return -EFAULT; | 590 | return -EFAULT; |
591 | } else if (offset < sizeof(struct icmphdr)) { | ||
592 | BUG(); | ||
593 | } else { | ||
594 | if (csum_partial_copy_fromiovecend | ||
595 | (to, pfh->iov, offset - sizeof(struct icmphdr), | ||
596 | fraglen, &pfh->wcheck)) | ||
597 | return -EFAULT; | ||
598 | } | ||
428 | 599 | ||
429 | return 0; | 600 | #if IS_ENABLED(CONFIG_IPV6) |
601 | /* For IPv6, checksum each skb as we go along, as expected by | ||
602 | * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in | ||
603 | * wcheck, it will be finalized in ping_v4_push_pending_frames. | ||
604 | */ | ||
605 | if (pfh->family == AF_INET6) { | ||
606 | skb->csum = pfh->wcheck; | ||
607 | skb->ip_summed = CHECKSUM_NONE; | ||
608 | pfh->wcheck = 0; | ||
430 | } | 609 | } |
431 | if (offset < sizeof(struct icmphdr)) | 610 | #endif |
432 | BUG(); | 611 | |
433 | if (csum_partial_copy_fromiovecend | ||
434 | (to, pfh->iov, offset - sizeof(struct icmphdr), | ||
435 | fraglen, &pfh->wcheck)) | ||
436 | return -EFAULT; | ||
437 | return 0; | 612 | return 0; |
438 | } | 613 | } |
614 | EXPORT_SYMBOL_GPL(ping_getfrag); | ||
439 | 615 | ||
440 | static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, | 616 | static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, |
441 | struct flowi4 *fl4) | 617 | struct flowi4 *fl4) |
442 | { | 618 | { |
443 | struct sk_buff *skb = skb_peek(&sk->sk_write_queue); | 619 | struct sk_buff *skb = skb_peek(&sk->sk_write_queue); |
444 | 620 | ||
@@ -450,24 +626,9 @@ static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, | |||
450 | return ip_push_pending_frames(sk, fl4); | 626 | return ip_push_pending_frames(sk, fl4); |
451 | } | 627 | } |
452 | 628 | ||
453 | static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 629 | int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, |
454 | size_t len) | 630 | void *user_icmph, size_t icmph_len) { |
455 | { | 631 | u8 type, code; |
456 | struct net *net = sock_net(sk); | ||
457 | struct flowi4 fl4; | ||
458 | struct inet_sock *inet = inet_sk(sk); | ||
459 | struct ipcm_cookie ipc; | ||
460 | struct icmphdr user_icmph; | ||
461 | struct pingfakehdr pfh; | ||
462 | struct rtable *rt = NULL; | ||
463 | struct ip_options_data opt_copy; | ||
464 | int free = 0; | ||
465 | __be32 saddr, daddr, faddr; | ||
466 | u8 tos; | ||
467 | int err; | ||
468 | |||
469 | pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); | ||
470 | |||
471 | 632 | ||
472 | if (len > 0xFFFF) | 633 | if (len > 0xFFFF) |
473 | return -EMSGSIZE; | 634 | return -EMSGSIZE; |
@@ -482,15 +643,53 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
482 | 643 | ||
483 | /* | 644 | /* |
484 | * Fetch the ICMP header provided by the userland. | 645 | * Fetch the ICMP header provided by the userland. |
485 | * iovec is modified! | 646 | * iovec is modified! The ICMP header is consumed. |
486 | */ | 647 | */ |
487 | 648 | if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len)) | |
488 | if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov, | ||
489 | sizeof(struct icmphdr))) | ||
490 | return -EFAULT; | 649 | return -EFAULT; |
491 | if (!ping_supported(user_icmph.type, user_icmph.code)) | 650 | |
651 | if (family == AF_INET) { | ||
652 | type = ((struct icmphdr *) user_icmph)->type; | ||
653 | code = ((struct icmphdr *) user_icmph)->code; | ||
654 | #if IS_ENABLED(CONFIG_IPV6) | ||
655 | } else if (family == AF_INET6) { | ||
656 | type = ((struct icmp6hdr *) user_icmph)->icmp6_type; | ||
657 | code = ((struct icmp6hdr *) user_icmph)->icmp6_code; | ||
658 | #endif | ||
659 | } else { | ||
660 | BUG(); | ||
661 | } | ||
662 | |||
663 | if (!ping_supported(family, type, code)) | ||
492 | return -EINVAL; | 664 | return -EINVAL; |
493 | 665 | ||
666 | return 0; | ||
667 | } | ||
668 | EXPORT_SYMBOL_GPL(ping_common_sendmsg); | ||
669 | |||
670 | int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
671 | size_t len) | ||
672 | { | ||
673 | struct net *net = sock_net(sk); | ||
674 | struct flowi4 fl4; | ||
675 | struct inet_sock *inet = inet_sk(sk); | ||
676 | struct ipcm_cookie ipc; | ||
677 | struct icmphdr user_icmph; | ||
678 | struct pingfakehdr pfh; | ||
679 | struct rtable *rt = NULL; | ||
680 | struct ip_options_data opt_copy; | ||
681 | int free = 0; | ||
682 | __be32 saddr, daddr, faddr; | ||
683 | u8 tos; | ||
684 | int err; | ||
685 | |||
686 | pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); | ||
687 | |||
688 | err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph, | ||
689 | sizeof(user_icmph)); | ||
690 | if (err) | ||
691 | return err; | ||
692 | |||
494 | /* | 693 | /* |
495 | * Get and verify the address. | 694 | * Get and verify the address. |
496 | */ | 695 | */ |
@@ -595,13 +794,14 @@ back_from_confirm: | |||
595 | pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; | 794 | pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; |
596 | pfh.iov = msg->msg_iov; | 795 | pfh.iov = msg->msg_iov; |
597 | pfh.wcheck = 0; | 796 | pfh.wcheck = 0; |
797 | pfh.family = AF_INET; | ||
598 | 798 | ||
599 | err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, | 799 | err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, |
600 | 0, &ipc, &rt, msg->msg_flags); | 800 | 0, &ipc, &rt, msg->msg_flags); |
601 | if (err) | 801 | if (err) |
602 | ip_flush_pending_frames(sk); | 802 | ip_flush_pending_frames(sk); |
603 | else | 803 | else |
604 | err = ping_push_pending_frames(sk, &pfh, &fl4); | 804 | err = ping_v4_push_pending_frames(sk, &pfh, &fl4); |
605 | release_sock(sk); | 805 | release_sock(sk); |
606 | 806 | ||
607 | out: | 807 | out: |
@@ -622,11 +822,13 @@ do_confirm: | |||
622 | goto out; | 822 | goto out; |
623 | } | 823 | } |
624 | 824 | ||
625 | static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 825 | int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, |
626 | size_t len, int noblock, int flags, int *addr_len) | 826 | size_t len, int noblock, int flags, int *addr_len) |
627 | { | 827 | { |
628 | struct inet_sock *isk = inet_sk(sk); | 828 | struct inet_sock *isk = inet_sk(sk); |
629 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; | 829 | int family = sk->sk_family; |
830 | struct sockaddr_in *sin; | ||
831 | struct sockaddr_in6 *sin6; | ||
630 | struct sk_buff *skb; | 832 | struct sk_buff *skb; |
631 | int copied, err; | 833 | int copied, err; |
632 | 834 | ||
@@ -636,11 +838,22 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
636 | if (flags & MSG_OOB) | 838 | if (flags & MSG_OOB) |
637 | goto out; | 839 | goto out; |
638 | 840 | ||
639 | if (addr_len) | 841 | if (addr_len) { |
640 | *addr_len = sizeof(*sin); | 842 | if (family == AF_INET) |
843 | *addr_len = sizeof(*sin); | ||
844 | else if (family == AF_INET6 && addr_len) | ||
845 | *addr_len = sizeof(*sin6); | ||
846 | } | ||
641 | 847 | ||
642 | if (flags & MSG_ERRQUEUE) | 848 | if (flags & MSG_ERRQUEUE) { |
643 | return ip_recv_error(sk, msg, len); | 849 | if (family == AF_INET) { |
850 | return ip_recv_error(sk, msg, len); | ||
851 | #if IS_ENABLED(CONFIG_IPV6) | ||
852 | } else if (family == AF_INET6) { | ||
853 | return pingv6_ops.ipv6_recv_error(sk, msg, len); | ||
854 | #endif | ||
855 | } | ||
856 | } | ||
644 | 857 | ||
645 | skb = skb_recv_datagram(sk, flags, noblock, &err); | 858 | skb = skb_recv_datagram(sk, flags, noblock, &err); |
646 | if (!skb) | 859 | if (!skb) |
@@ -659,15 +872,40 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
659 | 872 | ||
660 | sock_recv_timestamp(msg, sk, skb); | 873 | sock_recv_timestamp(msg, sk, skb); |
661 | 874 | ||
662 | /* Copy the address. */ | 875 | /* Copy the address and add cmsg data. */ |
663 | if (sin) { | 876 | if (family == AF_INET) { |
877 | sin = (struct sockaddr_in *) msg->msg_name; | ||
664 | sin->sin_family = AF_INET; | 878 | sin->sin_family = AF_INET; |
665 | sin->sin_port = 0 /* skb->h.uh->source */; | 879 | sin->sin_port = 0 /* skb->h.uh->source */; |
666 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 880 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
667 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); | 881 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
882 | |||
883 | if (isk->cmsg_flags) | ||
884 | ip_cmsg_recv(msg, skb); | ||
885 | |||
886 | #if IS_ENABLED(CONFIG_IPV6) | ||
887 | } else if (family == AF_INET6) { | ||
888 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
889 | struct ipv6hdr *ip6 = ipv6_hdr(skb); | ||
890 | sin6 = (struct sockaddr_in6 *) msg->msg_name; | ||
891 | sin6->sin6_family = AF_INET6; | ||
892 | sin6->sin6_port = 0; | ||
893 | sin6->sin6_addr = ip6->saddr; | ||
894 | |||
895 | if (np->sndflow) | ||
896 | sin6->sin6_flowinfo = ip6_flowinfo(ip6); | ||
897 | |||
898 | if (__ipv6_addr_needs_scope_id( | ||
899 | ipv6_addr_type(&sin6->sin6_addr))) | ||
900 | sin6->sin6_scope_id = IP6CB(skb)->iif; | ||
901 | |||
902 | if (inet6_sk(sk)->rxopt.all) | ||
903 | pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb); | ||
904 | #endif | ||
905 | } else { | ||
906 | BUG(); | ||
668 | } | 907 | } |
669 | if (isk->cmsg_flags) | 908 | |
670 | ip_cmsg_recv(msg, skb); | ||
671 | err = copied; | 909 | err = copied; |
672 | 910 | ||
673 | done: | 911 | done: |
@@ -676,8 +914,9 @@ out: | |||
676 | pr_debug("ping_recvmsg -> %d\n", err); | 914 | pr_debug("ping_recvmsg -> %d\n", err); |
677 | return err; | 915 | return err; |
678 | } | 916 | } |
917 | EXPORT_SYMBOL_GPL(ping_recvmsg); | ||
679 | 918 | ||
680 | static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | 919 | int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) |
681 | { | 920 | { |
682 | pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", | 921 | pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", |
683 | inet_sk(sk), inet_sk(sk)->inet_num, skb); | 922 | inet_sk(sk), inet_sk(sk)->inet_num, skb); |
@@ -688,6 +927,7 @@ static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
688 | } | 927 | } |
689 | return 0; | 928 | return 0; |
690 | } | 929 | } |
930 | EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); | ||
691 | 931 | ||
692 | 932 | ||
693 | /* | 933 | /* |
@@ -698,10 +938,7 @@ void ping_rcv(struct sk_buff *skb) | |||
698 | { | 938 | { |
699 | struct sock *sk; | 939 | struct sock *sk; |
700 | struct net *net = dev_net(skb->dev); | 940 | struct net *net = dev_net(skb->dev); |
701 | struct iphdr *iph = ip_hdr(skb); | ||
702 | struct icmphdr *icmph = icmp_hdr(skb); | 941 | struct icmphdr *icmph = icmp_hdr(skb); |
703 | __be32 saddr = iph->saddr; | ||
704 | __be32 daddr = iph->daddr; | ||
705 | 942 | ||
706 | /* We assume the packet has already been checked by icmp_rcv */ | 943 | /* We assume the packet has already been checked by icmp_rcv */ |
707 | 944 | ||
@@ -711,8 +948,7 @@ void ping_rcv(struct sk_buff *skb) | |||
711 | /* Push ICMP header back */ | 948 | /* Push ICMP header back */ |
712 | skb_push(skb, skb->data - (u8 *)icmph); | 949 | skb_push(skb, skb->data - (u8 *)icmph); |
713 | 950 | ||
714 | sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id), | 951 | sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); |
715 | skb->dev->ifindex); | ||
716 | if (sk != NULL) { | 952 | if (sk != NULL) { |
717 | pr_debug("rcv on socket %p\n", sk); | 953 | pr_debug("rcv on socket %p\n", sk); |
718 | ping_queue_rcv_skb(sk, skb_get(skb)); | 954 | ping_queue_rcv_skb(sk, skb_get(skb)); |
@@ -723,6 +959,7 @@ void ping_rcv(struct sk_buff *skb) | |||
723 | 959 | ||
724 | /* We're called from icmp_rcv(). kfree_skb() is done there. */ | 960 | /* We're called from icmp_rcv(). kfree_skb() is done there. */ |
725 | } | 961 | } |
962 | EXPORT_SYMBOL_GPL(ping_rcv); | ||
726 | 963 | ||
727 | struct proto ping_prot = { | 964 | struct proto ping_prot = { |
728 | .name = "PING", | 965 | .name = "PING", |
@@ -733,14 +970,14 @@ struct proto ping_prot = { | |||
733 | .disconnect = udp_disconnect, | 970 | .disconnect = udp_disconnect, |
734 | .setsockopt = ip_setsockopt, | 971 | .setsockopt = ip_setsockopt, |
735 | .getsockopt = ip_getsockopt, | 972 | .getsockopt = ip_getsockopt, |
736 | .sendmsg = ping_sendmsg, | 973 | .sendmsg = ping_v4_sendmsg, |
737 | .recvmsg = ping_recvmsg, | 974 | .recvmsg = ping_recvmsg, |
738 | .bind = ping_bind, | 975 | .bind = ping_bind, |
739 | .backlog_rcv = ping_queue_rcv_skb, | 976 | .backlog_rcv = ping_queue_rcv_skb, |
740 | .release_cb = ip4_datagram_release_cb, | 977 | .release_cb = ip4_datagram_release_cb, |
741 | .hash = ping_v4_hash, | 978 | .hash = ping_hash, |
742 | .unhash = ping_v4_unhash, | 979 | .unhash = ping_unhash, |
743 | .get_port = ping_v4_get_port, | 980 | .get_port = ping_get_port, |
744 | .obj_size = sizeof(struct inet_sock), | 981 | .obj_size = sizeof(struct inet_sock), |
745 | }; | 982 | }; |
746 | EXPORT_SYMBOL(ping_prot); | 983 | EXPORT_SYMBOL(ping_prot); |
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 9af088d2cdaa..470a9c008e9b 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -7,7 +7,7 @@ obj-$(CONFIG_IPV6) += ipv6.o | |||
7 | ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ | 7 | ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ |
8 | addrlabel.o \ | 8 | addrlabel.o \ |
9 | route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ | 9 | route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ |
10 | raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ | 10 | raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ |
11 | exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o | 11 | exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o |
12 | 12 | ||
13 | ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o | 13 | ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index ab5c7ad482cd..a5ac969aeefe 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <net/udp.h> | 49 | #include <net/udp.h> |
50 | #include <net/udplite.h> | 50 | #include <net/udplite.h> |
51 | #include <net/tcp.h> | 51 | #include <net/tcp.h> |
52 | #include <net/ping.h> | ||
52 | #include <net/protocol.h> | 53 | #include <net/protocol.h> |
53 | #include <net/inet_common.h> | 54 | #include <net/inet_common.h> |
54 | #include <net/route.h> | 55 | #include <net/route.h> |
@@ -840,6 +841,9 @@ static int __init inet6_init(void) | |||
840 | if (err) | 841 | if (err) |
841 | goto out_unregister_udplite_proto; | 842 | goto out_unregister_udplite_proto; |
842 | 843 | ||
844 | err = proto_register(&pingv6_prot, 1); | ||
845 | if (err) | ||
846 | goto out_unregister_ping_proto; | ||
843 | 847 | ||
844 | /* We MUST register RAW sockets before we create the ICMP6, | 848 | /* We MUST register RAW sockets before we create the ICMP6, |
845 | * IGMP6, or NDISC control sockets. | 849 | * IGMP6, or NDISC control sockets. |
@@ -930,6 +934,10 @@ static int __init inet6_init(void) | |||
930 | if (err) | 934 | if (err) |
931 | goto ipv6_packet_fail; | 935 | goto ipv6_packet_fail; |
932 | 936 | ||
937 | err = pingv6_init(); | ||
938 | if (err) | ||
939 | goto pingv6_fail; | ||
940 | |||
933 | #ifdef CONFIG_SYSCTL | 941 | #ifdef CONFIG_SYSCTL |
934 | err = ipv6_sysctl_register(); | 942 | err = ipv6_sysctl_register(); |
935 | if (err) | 943 | if (err) |
@@ -942,6 +950,8 @@ out: | |||
942 | sysctl_fail: | 950 | sysctl_fail: |
943 | ipv6_packet_cleanup(); | 951 | ipv6_packet_cleanup(); |
944 | #endif | 952 | #endif |
953 | pingv6_fail: | ||
954 | pingv6_exit(); | ||
945 | ipv6_packet_fail: | 955 | ipv6_packet_fail: |
946 | tcpv6_exit(); | 956 | tcpv6_exit(); |
947 | tcpv6_fail: | 957 | tcpv6_fail: |
@@ -985,6 +995,8 @@ register_pernet_fail: | |||
985 | rtnl_unregister_all(PF_INET6); | 995 | rtnl_unregister_all(PF_INET6); |
986 | out_sock_register_fail: | 996 | out_sock_register_fail: |
987 | rawv6_exit(); | 997 | rawv6_exit(); |
998 | out_unregister_ping_proto: | ||
999 | proto_unregister(&pingv6_prot); | ||
988 | out_unregister_raw_proto: | 1000 | out_unregister_raw_proto: |
989 | proto_unregister(&rawv6_prot); | 1001 | proto_unregister(&rawv6_prot); |
990 | out_unregister_udplite_proto: | 1002 | out_unregister_udplite_proto: |
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index b4ff0a42b8c7..1d2902e61786 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -57,6 +57,7 @@ | |||
57 | 57 | ||
58 | #include <net/ipv6.h> | 58 | #include <net/ipv6.h> |
59 | #include <net/ip6_checksum.h> | 59 | #include <net/ip6_checksum.h> |
60 | #include <net/ping.h> | ||
60 | #include <net/protocol.h> | 61 | #include <net/protocol.h> |
61 | #include <net/raw.h> | 62 | #include <net/raw.h> |
62 | #include <net/rawv6.h> | 63 | #include <net/rawv6.h> |
@@ -84,12 +85,18 @@ static inline struct sock *icmpv6_sk(struct net *net) | |||
84 | static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 85 | static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
85 | u8 type, u8 code, int offset, __be32 info) | 86 | u8 type, u8 code, int offset, __be32 info) |
86 | { | 87 | { |
88 | /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */ | ||
89 | struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset); | ||
87 | struct net *net = dev_net(skb->dev); | 90 | struct net *net = dev_net(skb->dev); |
88 | 91 | ||
89 | if (type == ICMPV6_PKT_TOOBIG) | 92 | if (type == ICMPV6_PKT_TOOBIG) |
90 | ip6_update_pmtu(skb, net, info, 0, 0); | 93 | ip6_update_pmtu(skb, net, info, 0, 0); |
91 | else if (type == NDISC_REDIRECT) | 94 | else if (type == NDISC_REDIRECT) |
92 | ip6_redirect(skb, net, 0, 0); | 95 | ip6_redirect(skb, net, 0, 0); |
96 | |||
97 | if (!(type & ICMPV6_INFOMSG_MASK)) | ||
98 | if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) | ||
99 | ping_err(skb, offset, info); | ||
93 | } | 100 | } |
94 | 101 | ||
95 | static int icmpv6_rcv(struct sk_buff *skb); | 102 | static int icmpv6_rcv(struct sk_buff *skb); |
@@ -224,7 +231,8 @@ static bool opt_unrec(struct sk_buff *skb, __u32 offset) | |||
224 | return (*op & 0xC0) == 0x80; | 231 | return (*op & 0xC0) == 0x80; |
225 | } | 232 | } |
226 | 233 | ||
227 | static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len) | 234 | int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, |
235 | struct icmp6hdr *thdr, int len) | ||
228 | { | 236 | { |
229 | struct sk_buff *skb; | 237 | struct sk_buff *skb; |
230 | struct icmp6hdr *icmp6h; | 238 | struct icmp6hdr *icmp6h; |
@@ -307,8 +315,8 @@ static void mip6_addr_swap(struct sk_buff *skb) | |||
307 | static inline void mip6_addr_swap(struct sk_buff *skb) {} | 315 | static inline void mip6_addr_swap(struct sk_buff *skb) {} |
308 | #endif | 316 | #endif |
309 | 317 | ||
310 | static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, | 318 | struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, |
311 | struct sock *sk, struct flowi6 *fl6) | 319 | struct sock *sk, struct flowi6 *fl6) |
312 | { | 320 | { |
313 | struct dst_entry *dst, *dst2; | 321 | struct dst_entry *dst, *dst2; |
314 | struct flowi6 fl2; | 322 | struct flowi6 fl2; |
@@ -697,7 +705,8 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
697 | skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len, | 705 | skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len, |
698 | IPPROTO_ICMPV6, 0)); | 706 | IPPROTO_ICMPV6, 0)); |
699 | if (__skb_checksum_complete(skb)) { | 707 | if (__skb_checksum_complete(skb)) { |
700 | LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]\n", | 708 | LIMIT_NETDEBUG(KERN_DEBUG |
709 | "ICMPv6 checksum failed [%pI6c > %pI6c]\n", | ||
701 | saddr, daddr); | 710 | saddr, daddr); |
702 | goto csum_error; | 711 | goto csum_error; |
703 | } | 712 | } |
@@ -718,7 +727,7 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
718 | break; | 727 | break; |
719 | 728 | ||
720 | case ICMPV6_ECHO_REPLY: | 729 | case ICMPV6_ECHO_REPLY: |
721 | /* we couldn't care less */ | 730 | ping_rcv(skb); |
722 | break; | 731 | break; |
723 | 732 | ||
724 | case ICMPV6_PKT_TOOBIG: | 733 | case ICMPV6_PKT_TOOBIG: |
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c new file mode 100644 index 000000000000..a6462d657c15 --- /dev/null +++ b/net/ipv6/ping.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * INET An implementation of the TCP/IP protocol suite for the LINUX | ||
3 | * operating system. INET is implemented using the BSD Socket | ||
4 | * interface as the means of communication with the user level. | ||
5 | * | ||
6 | * "Ping" sockets | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * Based on ipv4/ping.c code. | ||
14 | * | ||
15 | * Authors: Lorenzo Colitti (IPv6 support) | ||
16 | * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6), | ||
17 | * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32) | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <net/addrconf.h> | ||
22 | #include <net/ipv6.h> | ||
23 | #include <net/ip6_route.h> | ||
24 | #include <net/protocol.h> | ||
25 | #include <net/udp.h> | ||
26 | #include <net/transp_v6.h> | ||
27 | #include <net/ping.h> | ||
28 | |||
29 | struct proto pingv6_prot = { | ||
30 | .name = "PINGv6", | ||
31 | .owner = THIS_MODULE, | ||
32 | .init = ping_init_sock, | ||
33 | .close = ping_close, | ||
34 | .connect = ip6_datagram_connect, | ||
35 | .disconnect = udp_disconnect, | ||
36 | .setsockopt = ipv6_setsockopt, | ||
37 | .getsockopt = ipv6_getsockopt, | ||
38 | .sendmsg = ping_v6_sendmsg, | ||
39 | .recvmsg = ping_recvmsg, | ||
40 | .bind = ping_bind, | ||
41 | .backlog_rcv = ping_queue_rcv_skb, | ||
42 | .hash = ping_hash, | ||
43 | .unhash = ping_unhash, | ||
44 | .get_port = ping_get_port, | ||
45 | .obj_size = sizeof(struct raw6_sock), | ||
46 | }; | ||
47 | EXPORT_SYMBOL_GPL(pingv6_prot); | ||
48 | |||
49 | static struct inet_protosw pingv6_protosw = { | ||
50 | .type = SOCK_DGRAM, | ||
51 | .protocol = IPPROTO_ICMPV6, | ||
52 | .prot = &pingv6_prot, | ||
53 | .ops = &inet6_dgram_ops, | ||
54 | .no_check = UDP_CSUM_DEFAULT, | ||
55 | .flags = INET_PROTOSW_REUSE, | ||
56 | }; | ||
57 | |||
58 | |||
59 | /* Compatibility glue so we can support IPv6 when it's compiled as a module */ | ||
60 | int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) | ||
61 | { | ||
62 | return -EAFNOSUPPORT; | ||
63 | } | ||
64 | int dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, | ||
65 | struct sk_buff *skb) | ||
66 | { | ||
67 | return -EAFNOSUPPORT; | ||
68 | } | ||
69 | int dummy_icmpv6_err_convert(u8 type, u8 code, int *err) | ||
70 | { | ||
71 | return -EAFNOSUPPORT; | ||
72 | } | ||
73 | void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, | ||
74 | __be16 port, u32 info, u8 *payload) {} | ||
75 | int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr, | ||
76 | struct net_device *dev, int strict) | ||
77 | { | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | int __init pingv6_init(void) | ||
82 | { | ||
83 | pingv6_ops.ipv6_recv_error = ipv6_recv_error; | ||
84 | pingv6_ops.ip6_datagram_recv_ctl = ip6_datagram_recv_ctl; | ||
85 | pingv6_ops.icmpv6_err_convert = icmpv6_err_convert; | ||
86 | pingv6_ops.ipv6_icmp_error = ipv6_icmp_error; | ||
87 | pingv6_ops.ipv6_chk_addr = ipv6_chk_addr; | ||
88 | return inet6_register_protosw(&pingv6_protosw); | ||
89 | } | ||
90 | |||
91 | /* This never gets called because it's not possible to unload the ipv6 module, | ||
92 | * but just in case. | ||
93 | */ | ||
94 | void pingv6_exit(void) | ||
95 | { | ||
96 | pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error; | ||
97 | pingv6_ops.ip6_datagram_recv_ctl = dummy_ip6_datagram_recv_ctl; | ||
98 | pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert; | ||
99 | pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error; | ||
100 | pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr; | ||
101 | inet6_unregister_protosw(&pingv6_protosw); | ||
102 | } | ||
103 | |||
104 | int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | ||
105 | size_t len) | ||
106 | { | ||
107 | struct inet_sock *inet = inet_sk(sk); | ||
108 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
109 | struct icmp6hdr user_icmph; | ||
110 | int addr_type; | ||
111 | struct in6_addr *daddr; | ||
112 | int iif = 0; | ||
113 | struct flowi6 fl6; | ||
114 | int err; | ||
115 | int hlimit; | ||
116 | struct dst_entry *dst; | ||
117 | struct rt6_info *rt; | ||
118 | struct pingfakehdr pfh; | ||
119 | |||
120 | pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); | ||
121 | |||
122 | err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, | ||
123 | sizeof(user_icmph)); | ||
124 | if (err) | ||
125 | return err; | ||
126 | |||
127 | if (msg->msg_name) { | ||
128 | struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name; | ||
129 | if (msg->msg_namelen < sizeof(struct sockaddr_in6) || | ||
130 | u->sin6_family != AF_INET6) { | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | if (sk->sk_bound_dev_if && | ||
134 | sk->sk_bound_dev_if != u->sin6_scope_id) { | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | daddr = &(u->sin6_addr); | ||
138 | iif = u->sin6_scope_id; | ||
139 | } else { | ||
140 | if (sk->sk_state != TCP_ESTABLISHED) | ||
141 | return -EDESTADDRREQ; | ||
142 | daddr = &np->daddr; | ||
143 | } | ||
144 | |||
145 | if (!iif) | ||
146 | iif = sk->sk_bound_dev_if; | ||
147 | |||
148 | addr_type = ipv6_addr_type(daddr); | ||
149 | if (__ipv6_addr_needs_scope_id(addr_type) && !iif) | ||
150 | return -EINVAL; | ||
151 | if (addr_type & IPV6_ADDR_MAPPED) | ||
152 | return -EINVAL; | ||
153 | |||
154 | /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ | ||
155 | |||
156 | memset(&fl6, 0, sizeof(fl6)); | ||
157 | |||
158 | fl6.flowi6_proto = IPPROTO_ICMPV6; | ||
159 | fl6.saddr = np->saddr; | ||
160 | fl6.daddr = *daddr; | ||
161 | fl6.fl6_icmp_type = user_icmph.icmp6_type; | ||
162 | fl6.fl6_icmp_code = user_icmph.icmp6_code; | ||
163 | security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); | ||
164 | |||
165 | if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) | ||
166 | fl6.flowi6_oif = np->mcast_oif; | ||
167 | else if (!fl6.flowi6_oif) | ||
168 | fl6.flowi6_oif = np->ucast_oif; | ||
169 | |||
170 | dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1); | ||
171 | if (IS_ERR(dst)) | ||
172 | return PTR_ERR(dst); | ||
173 | rt = (struct rt6_info *) dst; | ||
174 | |||
175 | np = inet6_sk(sk); | ||
176 | if (!np) | ||
177 | return -EBADF; | ||
178 | |||
179 | if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) | ||
180 | fl6.flowi6_oif = np->mcast_oif; | ||
181 | else if (!fl6.flowi6_oif) | ||
182 | fl6.flowi6_oif = np->ucast_oif; | ||
183 | |||
184 | pfh.icmph.type = user_icmph.icmp6_type; | ||
185 | pfh.icmph.code = user_icmph.icmp6_code; | ||
186 | pfh.icmph.checksum = 0; | ||
187 | pfh.icmph.un.echo.id = inet->inet_sport; | ||
188 | pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; | ||
189 | pfh.iov = msg->msg_iov; | ||
190 | pfh.wcheck = 0; | ||
191 | pfh.family = AF_INET6; | ||
192 | |||
193 | if (ipv6_addr_is_multicast(&fl6.daddr)) | ||
194 | hlimit = np->mcast_hops; | ||
195 | else | ||
196 | hlimit = np->hop_limit; | ||
197 | if (hlimit < 0) | ||
198 | hlimit = ip6_dst_hoplimit(dst); | ||
199 | |||
200 | err = ip6_append_data(sk, ping_getfrag, &pfh, len, | ||
201 | 0, hlimit, | ||
202 | np->tclass, NULL, &fl6, rt, | ||
203 | MSG_DONTWAIT, np->dontfrag); | ||
204 | |||
205 | if (err) { | ||
206 | ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev, | ||
207 | ICMP6_MIB_OUTERRORS); | ||
208 | ip6_flush_pending_frames(sk); | ||
209 | } else { | ||
210 | err = icmpv6_push_pending_frames(sk, &fl6, | ||
211 | (struct icmp6hdr *) &pfh.icmph, | ||
212 | len); | ||
213 | } | ||
214 | |||
215 | return err; | ||
216 | } | ||