|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2006 Oracle. All rights reserved. |
| 2 | + * Copyright (c) 2006, 2018 Oracle and/or its affiliates. All rights reserved. |
3 | 3 | *
|
4 | 4 | * This software is available to you under a choice of one of two
|
5 | 5 | * licenses. You may choose to be licensed under the terms of the GNU
|
|
35 | 35 | #include <linux/kernel.h>
|
36 | 36 | #include <linux/gfp.h>
|
37 | 37 | #include <linux/in.h>
|
| 38 | +#include <linux/ipv6.h> |
38 | 39 | #include <linux/poll.h>
|
39 | 40 | #include <net/sock.h>
|
40 | 41 |
|
@@ -113,26 +114,63 @@ void rds_wake_sk_sleep(struct rds_sock *rs)
|
113 | 114 | static int rds_getname(struct socket *sock, struct sockaddr *uaddr,
|
114 | 115 | int peer)
|
115 | 116 | {
|
116 |
| - struct sockaddr_in *sin = (struct sockaddr_in *)uaddr; |
117 | 117 | struct rds_sock *rs = rds_sk_to_rs(sock->sk);
|
118 |
| - |
119 |
| - memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 118 | + struct sockaddr_in6 *sin6; |
| 119 | + struct sockaddr_in *sin; |
| 120 | + int uaddr_len; |
120 | 121 |
|
121 | 122 | /* racey, don't care */
|
122 | 123 | if (peer) {
|
123 |
| - if (!rs->rs_conn_addr) |
| 124 | + if (ipv6_addr_any(&rs->rs_conn_addr)) |
124 | 125 | return -ENOTCONN;
|
125 | 126 |
|
126 |
| - sin->sin_port = rs->rs_conn_port; |
127 |
| - sin->sin_addr.s_addr = rs->rs_conn_addr; |
| 127 | + if (ipv6_addr_v4mapped(&rs->rs_conn_addr)) { |
| 128 | + sin = (struct sockaddr_in *)uaddr; |
| 129 | + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 130 | + sin->sin_family = AF_INET; |
| 131 | + sin->sin_port = rs->rs_conn_port; |
| 132 | + sin->sin_addr.s_addr = rs->rs_conn_addr_v4; |
| 133 | + uaddr_len = sizeof(*sin); |
| 134 | + } else { |
| 135 | + sin6 = (struct sockaddr_in6 *)uaddr; |
| 136 | + sin6->sin6_family = AF_INET6; |
| 137 | + sin6->sin6_port = rs->rs_conn_port; |
| 138 | + sin6->sin6_addr = rs->rs_conn_addr; |
| 139 | + sin6->sin6_flowinfo = 0; |
| 140 | + /* scope_id is the same as in the bound address. */ |
| 141 | + sin6->sin6_scope_id = rs->rs_bound_scope_id; |
| 142 | + uaddr_len = sizeof(*sin6); |
| 143 | + } |
128 | 144 | } else {
|
129 |
| - sin->sin_port = rs->rs_bound_port; |
130 |
| - sin->sin_addr.s_addr = rs->rs_bound_addr; |
| 145 | + /* If socket is not yet bound, set the return address family |
| 146 | + * to be AF_UNSPEC (value 0) and the address size to be that |
| 147 | + * of an IPv4 address. |
| 148 | + */ |
| 149 | + if (ipv6_addr_any(&rs->rs_bound_addr)) { |
| 150 | + sin = (struct sockaddr_in *)uaddr; |
| 151 | + memset(sin, 0, sizeof(*sin)); |
| 152 | + sin->sin_family = AF_UNSPEC; |
| 153 | + return sizeof(*sin); |
| 154 | + } |
| 155 | + if (ipv6_addr_v4mapped(&rs->rs_bound_addr)) { |
| 156 | + sin = (struct sockaddr_in *)uaddr; |
| 157 | + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 158 | + sin->sin_family = AF_INET; |
| 159 | + sin->sin_port = rs->rs_bound_port; |
| 160 | + sin->sin_addr.s_addr = rs->rs_bound_addr_v4; |
| 161 | + uaddr_len = sizeof(*sin); |
| 162 | + } else { |
| 163 | + sin6 = (struct sockaddr_in6 *)uaddr; |
| 164 | + sin6->sin6_family = AF_INET6; |
| 165 | + sin6->sin6_port = rs->rs_bound_port; |
| 166 | + sin6->sin6_addr = rs->rs_bound_addr; |
| 167 | + sin6->sin6_flowinfo = 0; |
| 168 | + sin6->sin6_scope_id = rs->rs_bound_scope_id; |
| 169 | + uaddr_len = sizeof(*sin6); |
| 170 | + } |
131 | 171 | }
|
132 | 172 |
|
133 |
| - sin->sin_family = AF_INET; |
134 |
| - |
135 |
| - return sizeof(*sin); |
| 173 | + return uaddr_len; |
136 | 174 | }
|
137 | 175 |
|
138 | 176 | /*
|
@@ -203,26 +241,36 @@ static int rds_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
203 | 241 | static int rds_cancel_sent_to(struct rds_sock *rs, char __user *optval,
|
204 | 242 | int len)
|
205 | 243 | {
|
| 244 | + struct sockaddr_in6 sin6; |
206 | 245 | struct sockaddr_in sin;
|
207 | 246 | int ret = 0;
|
208 | 247 |
|
209 | 248 | /* racing with another thread binding seems ok here */
|
210 |
| - if (rs->rs_bound_addr == 0) { |
| 249 | + if (ipv6_addr_any(&rs->rs_bound_addr)) { |
211 | 250 | ret = -ENOTCONN; /* XXX not a great errno */
|
212 | 251 | goto out;
|
213 | 252 | }
|
214 | 253 |
|
215 | 254 | if (len < sizeof(struct sockaddr_in)) {
|
216 | 255 | ret = -EINVAL;
|
217 | 256 | goto out;
|
| 257 | + } else if (len < sizeof(struct sockaddr_in6)) { |
| 258 | + /* Assume IPv4 */ |
| 259 | + if (copy_from_user(&sin, optval, sizeof(struct sockaddr_in))) { |
| 260 | + ret = -EFAULT; |
| 261 | + goto out; |
| 262 | + } |
| 263 | + ipv6_addr_set_v4mapped(sin.sin_addr.s_addr, &sin6.sin6_addr); |
| 264 | + sin6.sin6_port = sin.sin_port; |
| 265 | + } else { |
| 266 | + if (copy_from_user(&sin6, optval, |
| 267 | + sizeof(struct sockaddr_in6))) { |
| 268 | + ret = -EFAULT; |
| 269 | + goto out; |
| 270 | + } |
218 | 271 | }
|
219 | 272 |
|
220 |
| - if (copy_from_user(&sin, optval, sizeof(sin))) { |
221 |
| - ret = -EFAULT; |
222 |
| - goto out; |
223 |
| - } |
224 |
| - |
225 |
| - rds_send_drop_to(rs, &sin); |
| 273 | + rds_send_drop_to(rs, &sin6); |
226 | 274 | out:
|
227 | 275 | return ret;
|
228 | 276 | }
|
@@ -435,31 +483,41 @@ static int rds_connect(struct socket *sock, struct sockaddr *uaddr,
|
435 | 483 | int addr_len, int flags)
|
436 | 484 | {
|
437 | 485 | struct sock *sk = sock->sk;
|
438 |
| - struct sockaddr_in *sin = (struct sockaddr_in *)uaddr; |
| 486 | + struct sockaddr_in *sin; |
439 | 487 | struct rds_sock *rs = rds_sk_to_rs(sk);
|
440 | 488 | int ret = 0;
|
441 | 489 |
|
442 | 490 | lock_sock(sk);
|
443 | 491 |
|
444 |
| - if (addr_len != sizeof(struct sockaddr_in)) { |
445 |
| - ret = -EINVAL; |
446 |
| - goto out; |
447 |
| - } |
| 492 | + switch (addr_len) { |
| 493 | + case sizeof(struct sockaddr_in): |
| 494 | + sin = (struct sockaddr_in *)uaddr; |
| 495 | + if (sin->sin_family != AF_INET) { |
| 496 | + ret = -EAFNOSUPPORT; |
| 497 | + break; |
| 498 | + } |
| 499 | + if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) { |
| 500 | + ret = -EDESTADDRREQ; |
| 501 | + break; |
| 502 | + } |
| 503 | + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) || |
| 504 | + sin->sin_addr.s_addr == htonl(INADDR_BROADCAST)) { |
| 505 | + ret = -EINVAL; |
| 506 | + break; |
| 507 | + } |
| 508 | + ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr); |
| 509 | + rs->rs_conn_port = sin->sin_port; |
| 510 | + break; |
448 | 511 |
|
449 |
| - if (sin->sin_family != AF_INET) { |
450 |
| - ret = -EAFNOSUPPORT; |
451 |
| - goto out; |
452 |
| - } |
| 512 | + case sizeof(struct sockaddr_in6): |
| 513 | + ret = -EPROTONOSUPPORT; |
| 514 | + break; |
453 | 515 |
|
454 |
| - if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) { |
455 |
| - ret = -EDESTADDRREQ; |
456 |
| - goto out; |
| 516 | + default: |
| 517 | + ret = -EINVAL; |
| 518 | + break; |
457 | 519 | }
|
458 | 520 |
|
459 |
| - rs->rs_conn_addr = sin->sin_addr.s_addr; |
460 |
| - rs->rs_conn_port = sin->sin_port; |
461 |
| - |
462 |
| -out: |
463 | 521 | release_sock(sk);
|
464 | 522 | return ret;
|
465 | 523 | }
|
@@ -578,8 +636,10 @@ static void rds_sock_inc_info(struct socket *sock, unsigned int len,
|
578 | 636 | list_for_each_entry(inc, &rs->rs_recv_queue, i_item) {
|
579 | 637 | total++;
|
580 | 638 | if (total <= len)
|
581 |
| - rds_inc_info_copy(inc, iter, inc->i_saddr, |
582 |
| - rs->rs_bound_addr, 1); |
| 639 | + rds_inc_info_copy(inc, iter, |
| 640 | + inc->i_saddr.s6_addr32[3], |
| 641 | + rs->rs_bound_addr_v4, |
| 642 | + 1); |
583 | 643 | }
|
584 | 644 |
|
585 | 645 | read_unlock(&rs->rs_recv_lock);
|
@@ -608,8 +668,8 @@ static void rds_sock_info(struct socket *sock, unsigned int len,
|
608 | 668 | list_for_each_entry(rs, &rds_sock_list, rs_item) {
|
609 | 669 | sinfo.sndbuf = rds_sk_sndbuf(rs);
|
610 | 670 | sinfo.rcvbuf = rds_sk_rcvbuf(rs);
|
611 |
| - sinfo.bound_addr = rs->rs_bound_addr; |
612 |
| - sinfo.connected_addr = rs->rs_conn_addr; |
| 671 | + sinfo.bound_addr = rs->rs_bound_addr_v4; |
| 672 | + sinfo.connected_addr = rs->rs_conn_addr_v4; |
613 | 673 | sinfo.bound_port = rs->rs_bound_port;
|
614 | 674 | sinfo.connected_port = rs->rs_conn_port;
|
615 | 675 | sinfo.inum = sock_i_ino(rds_rs_to_sk(rs));
|
|
0 commit comments