diff options
author | Andrey Ignatov <rdna@fb.com> | 2018-03-30 18:08:04 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-03-30 20:15:43 -0400 |
commit | 3679d585bbc07a1ac4448d5b478b492cad3587ce (patch) | |
tree | 15cfea104f4ee4dcb55005a2ca101631eefa3f2f | |
parent | e50b0a6f089308bec6b2d0198abed231dee4d277 (diff) |
net: Introduce __inet_bind() and __inet6_bind
Refactor `bind()` code to make it ready to be called from BPF helper
function `bpf_bind()` (will be added soon). Implementation of
`inet_bind()` and `inet6_bind()` is separated into `__inet_bind()` and
`__inet6_bind()` correspondingly. These function can be used from both
`sk_prot->bind` and `bpf_bind()` contexts.
New functions have two additional arguments.
`force_bind_address_no_port` forces binding to IP only w/o checking
`inet_sock.bind_address_no_port` field. It'll allow to bind local end of
a connection to desired IP in `bpf_bind()` w/o changing
`bind_address_no_port` field of a socket. It's useful since `bpf_bind()`
can return an error and we'd need to restore original value of
`bind_address_no_port` in that case if we changed this before calling to
the helper.
`with_lock` specifies whether to lock socket when working with `struct
sk` or not. The argument is set to `true` for `sk_prot->bind`, i.e. old
behavior is preserved. But it will be set to `false` for `bpf_bind()`
use-case. The reason is all call-sites, where `bpf_bind()` will be
called, already hold that socket lock.
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r-- | include/net/inet_common.h | 2 | ||||
-rw-r--r-- | include/net/ipv6.h | 2 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 39 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 37 |
4 files changed, 52 insertions, 28 deletions
diff --git a/include/net/inet_common.h b/include/net/inet_common.h index 500f81375200..384b90c62c0b 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h | |||
@@ -32,6 +32,8 @@ int inet_shutdown(struct socket *sock, int how); | |||
32 | int inet_listen(struct socket *sock, int backlog); | 32 | int inet_listen(struct socket *sock, int backlog); |
33 | void inet_sock_destruct(struct sock *sk); | 33 | void inet_sock_destruct(struct sock *sk); |
34 | int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); | 34 | int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); |
35 | int __inet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, | ||
36 | bool force_bind_address_no_port, bool with_lock); | ||
35 | int inet_getname(struct socket *sock, struct sockaddr *uaddr, | 37 | int inet_getname(struct socket *sock, struct sockaddr *uaddr, |
36 | int peer); | 38 | int peer); |
37 | int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); | 39 | int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); |
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 50a6f0ddb878..2e5fedc56e59 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -1066,6 +1066,8 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); | |||
1066 | void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu); | 1066 | void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu); |
1067 | 1067 | ||
1068 | int inet6_release(struct socket *sock); | 1068 | int inet6_release(struct socket *sock); |
1069 | int __inet6_bind(struct sock *sock, struct sockaddr *uaddr, int addr_len, | ||
1070 | bool force_bind_address_no_port, bool with_lock); | ||
1069 | int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); | 1071 | int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); |
1070 | int inet6_getname(struct socket *sock, struct sockaddr *uaddr, | 1072 | int inet6_getname(struct socket *sock, struct sockaddr *uaddr, |
1071 | int peer); | 1073 | int peer); |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 2dec266507dc..e203a39d6988 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -432,30 +432,37 @@ EXPORT_SYMBOL(inet_release); | |||
432 | 432 | ||
433 | int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | 433 | int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) |
434 | { | 434 | { |
435 | struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; | ||
436 | struct sock *sk = sock->sk; | 435 | struct sock *sk = sock->sk; |
437 | struct inet_sock *inet = inet_sk(sk); | ||
438 | struct net *net = sock_net(sk); | ||
439 | unsigned short snum; | ||
440 | int chk_addr_ret; | ||
441 | u32 tb_id = RT_TABLE_LOCAL; | ||
442 | int err; | 436 | int err; |
443 | 437 | ||
444 | /* If the socket has its own bind function then use it. (RAW) */ | 438 | /* If the socket has its own bind function then use it. (RAW) */ |
445 | if (sk->sk_prot->bind) { | 439 | if (sk->sk_prot->bind) { |
446 | err = sk->sk_prot->bind(sk, uaddr, addr_len); | 440 | return sk->sk_prot->bind(sk, uaddr, addr_len); |
447 | goto out; | ||
448 | } | 441 | } |
449 | err = -EINVAL; | ||
450 | if (addr_len < sizeof(struct sockaddr_in)) | 442 | if (addr_len < sizeof(struct sockaddr_in)) |
451 | goto out; | 443 | return -EINVAL; |
452 | 444 | ||
453 | /* BPF prog is run before any checks are done so that if the prog | 445 | /* BPF prog is run before any checks are done so that if the prog |
454 | * changes context in a wrong way it will be caught. | 446 | * changes context in a wrong way it will be caught. |
455 | */ | 447 | */ |
456 | err = BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr); | 448 | err = BPF_CGROUP_RUN_PROG_INET4_BIND(sk, uaddr); |
457 | if (err) | 449 | if (err) |
458 | goto out; | 450 | return err; |
451 | |||
452 | return __inet_bind(sk, uaddr, addr_len, false, true); | ||
453 | } | ||
454 | EXPORT_SYMBOL(inet_bind); | ||
455 | |||
456 | int __inet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, | ||
457 | bool force_bind_address_no_port, bool with_lock) | ||
458 | { | ||
459 | struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; | ||
460 | struct inet_sock *inet = inet_sk(sk); | ||
461 | struct net *net = sock_net(sk); | ||
462 | unsigned short snum; | ||
463 | int chk_addr_ret; | ||
464 | u32 tb_id = RT_TABLE_LOCAL; | ||
465 | int err; | ||
459 | 466 | ||
460 | if (addr->sin_family != AF_INET) { | 467 | if (addr->sin_family != AF_INET) { |
461 | /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET) | 468 | /* Compatibility games : accept AF_UNSPEC (mapped to AF_INET) |
@@ -499,7 +506,8 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
499 | * would be illegal to use them (multicast/broadcast) in | 506 | * would be illegal to use them (multicast/broadcast) in |
500 | * which case the sending device address is used. | 507 | * which case the sending device address is used. |
501 | */ | 508 | */ |
502 | lock_sock(sk); | 509 | if (with_lock) |
510 | lock_sock(sk); | ||
503 | 511 | ||
504 | /* Check these errors (active socket, double bind). */ | 512 | /* Check these errors (active socket, double bind). */ |
505 | err = -EINVAL; | 513 | err = -EINVAL; |
@@ -511,7 +519,8 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
511 | inet->inet_saddr = 0; /* Use device */ | 519 | inet->inet_saddr = 0; /* Use device */ |
512 | 520 | ||
513 | /* Make sure we are allowed to bind here. */ | 521 | /* Make sure we are allowed to bind here. */ |
514 | if ((snum || !inet->bind_address_no_port) && | 522 | if ((snum || !(inet->bind_address_no_port || |
523 | force_bind_address_no_port)) && | ||
515 | sk->sk_prot->get_port(sk, snum)) { | 524 | sk->sk_prot->get_port(sk, snum)) { |
516 | inet->inet_saddr = inet->inet_rcv_saddr = 0; | 525 | inet->inet_saddr = inet->inet_rcv_saddr = 0; |
517 | err = -EADDRINUSE; | 526 | err = -EADDRINUSE; |
@@ -528,11 +537,11 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
528 | sk_dst_reset(sk); | 537 | sk_dst_reset(sk); |
529 | err = 0; | 538 | err = 0; |
530 | out_release_sock: | 539 | out_release_sock: |
531 | release_sock(sk); | 540 | if (with_lock) |
541 | release_sock(sk); | ||
532 | out: | 542 | out: |
533 | return err; | 543 | return err; |
534 | } | 544 | } |
535 | EXPORT_SYMBOL(inet_bind); | ||
536 | 545 | ||
537 | int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, | 546 | int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, |
538 | int addr_len, int flags) | 547 | int addr_len, int flags) |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index fa24e3f06ac6..13110bee5c14 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -277,15 +277,7 @@ out_rcu_unlock: | |||
277 | /* bind for INET6 API */ | 277 | /* bind for INET6 API */ |
278 | int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | 278 | int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) |
279 | { | 279 | { |
280 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)uaddr; | ||
281 | struct sock *sk = sock->sk; | 280 | struct sock *sk = sock->sk; |
282 | struct inet_sock *inet = inet_sk(sk); | ||
283 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
284 | struct net *net = sock_net(sk); | ||
285 | __be32 v4addr = 0; | ||
286 | unsigned short snum; | ||
287 | bool saved_ipv6only; | ||
288 | int addr_type = 0; | ||
289 | int err = 0; | 281 | int err = 0; |
290 | 282 | ||
291 | /* If the socket has its own bind function then use it. */ | 283 | /* If the socket has its own bind function then use it. */ |
@@ -302,11 +294,28 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
302 | if (err) | 294 | if (err) |
303 | return err; | 295 | return err; |
304 | 296 | ||
297 | return __inet6_bind(sk, uaddr, addr_len, false, true); | ||
298 | } | ||
299 | EXPORT_SYMBOL(inet6_bind); | ||
300 | |||
301 | int __inet6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len, | ||
302 | bool force_bind_address_no_port, bool with_lock) | ||
303 | { | ||
304 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)uaddr; | ||
305 | struct inet_sock *inet = inet_sk(sk); | ||
306 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
307 | struct net *net = sock_net(sk); | ||
308 | __be32 v4addr = 0; | ||
309 | unsigned short snum; | ||
310 | bool saved_ipv6only; | ||
311 | int addr_type = 0; | ||
312 | int err = 0; | ||
313 | |||
305 | if (addr->sin6_family != AF_INET6) | 314 | if (addr->sin6_family != AF_INET6) |
306 | return -EAFNOSUPPORT; | 315 | return -EAFNOSUPPORT; |
307 | 316 | ||
308 | addr_type = ipv6_addr_type(&addr->sin6_addr); | 317 | addr_type = ipv6_addr_type(&addr->sin6_addr); |
309 | if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) | 318 | if ((addr_type & IPV6_ADDR_MULTICAST) && sk->sk_type == SOCK_STREAM) |
310 | return -EINVAL; | 319 | return -EINVAL; |
311 | 320 | ||
312 | snum = ntohs(addr->sin6_port); | 321 | snum = ntohs(addr->sin6_port); |
@@ -314,7 +323,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
314 | !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) | 323 | !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) |
315 | return -EACCES; | 324 | return -EACCES; |
316 | 325 | ||
317 | lock_sock(sk); | 326 | if (with_lock) |
327 | lock_sock(sk); | ||
318 | 328 | ||
319 | /* Check these errors (active socket, double bind). */ | 329 | /* Check these errors (active socket, double bind). */ |
320 | if (sk->sk_state != TCP_CLOSE || inet->inet_num) { | 330 | if (sk->sk_state != TCP_CLOSE || inet->inet_num) { |
@@ -402,7 +412,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
402 | sk->sk_ipv6only = 1; | 412 | sk->sk_ipv6only = 1; |
403 | 413 | ||
404 | /* Make sure we are allowed to bind here. */ | 414 | /* Make sure we are allowed to bind here. */ |
405 | if ((snum || !inet->bind_address_no_port) && | 415 | if ((snum || !(inet->bind_address_no_port || |
416 | force_bind_address_no_port)) && | ||
406 | sk->sk_prot->get_port(sk, snum)) { | 417 | sk->sk_prot->get_port(sk, snum)) { |
407 | sk->sk_ipv6only = saved_ipv6only; | 418 | sk->sk_ipv6only = saved_ipv6only; |
408 | inet_reset_saddr(sk); | 419 | inet_reset_saddr(sk); |
@@ -418,13 +429,13 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
418 | inet->inet_dport = 0; | 429 | inet->inet_dport = 0; |
419 | inet->inet_daddr = 0; | 430 | inet->inet_daddr = 0; |
420 | out: | 431 | out: |
421 | release_sock(sk); | 432 | if (with_lock) |
433 | release_sock(sk); | ||
422 | return err; | 434 | return err; |
423 | out_unlock: | 435 | out_unlock: |
424 | rcu_read_unlock(); | 436 | rcu_read_unlock(); |
425 | goto out; | 437 | goto out; |
426 | } | 438 | } |
427 | EXPORT_SYMBOL(inet6_bind); | ||
428 | 439 | ||
429 | int inet6_release(struct socket *sock) | 440 | int inet6_release(struct socket *sock) |
430 | { | 441 | { |