diff options
| -rw-r--r-- | include/uapi/linux/fou.h | 1 | ||||
| -rw-r--r-- | net/ipv4/fou.c | 230 |
2 files changed, 186 insertions, 45 deletions
diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h index c303588bb767..d2947c52dc67 100644 --- a/include/uapi/linux/fou.h +++ b/include/uapi/linux/fou.h | |||
| @@ -25,6 +25,7 @@ enum { | |||
| 25 | FOU_CMD_UNSPEC, | 25 | FOU_CMD_UNSPEC, |
| 26 | FOU_CMD_ADD, | 26 | FOU_CMD_ADD, |
| 27 | FOU_CMD_DEL, | 27 | FOU_CMD_DEL, |
| 28 | FOU_CMD_GET, | ||
| 28 | 29 | ||
| 29 | __FOU_CMD_MAX, | 30 | __FOU_CMD_MAX, |
| 30 | }; | 31 | }; |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index ff069f6597ac..263710259774 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
| @@ -16,14 +16,12 @@ | |||
| 16 | #include <uapi/linux/fou.h> | 16 | #include <uapi/linux/fou.h> |
| 17 | #include <uapi/linux/genetlink.h> | 17 | #include <uapi/linux/genetlink.h> |
| 18 | 18 | ||
| 19 | static DEFINE_SPINLOCK(fou_lock); | ||
| 20 | static LIST_HEAD(fou_list); | ||
| 21 | |||
| 22 | struct fou { | 19 | struct fou { |
| 23 | struct socket *sock; | 20 | struct socket *sock; |
| 24 | u8 protocol; | 21 | u8 protocol; |
| 25 | u8 flags; | 22 | u8 flags; |
| 26 | u16 port; | 23 | __be16 port; |
| 24 | u16 type; | ||
| 27 | struct udp_offload udp_offloads; | 25 | struct udp_offload udp_offloads; |
| 28 | struct list_head list; | 26 | struct list_head list; |
| 29 | }; | 27 | }; |
| @@ -37,6 +35,13 @@ struct fou_cfg { | |||
| 37 | struct udp_port_cfg udp_config; | 35 | struct udp_port_cfg udp_config; |
| 38 | }; | 36 | }; |
| 39 | 37 | ||
| 38 | static unsigned int fou_net_id; | ||
| 39 | |||
| 40 | struct fou_net { | ||
| 41 | struct list_head fou_list; | ||
| 42 | struct mutex fou_lock; | ||
| 43 | }; | ||
| 44 | |||
| 40 | static inline struct fou *fou_from_sock(struct sock *sk) | 45 | static inline struct fou *fou_from_sock(struct sock *sk) |
| 41 | { | 46 | { |
| 42 | return sk->sk_user_data; | 47 | return sk->sk_user_data; |
| @@ -387,20 +392,21 @@ out_unlock: | |||
| 387 | return err; | 392 | return err; |
| 388 | } | 393 | } |
| 389 | 394 | ||
| 390 | static int fou_add_to_port_list(struct fou *fou) | 395 | static int fou_add_to_port_list(struct net *net, struct fou *fou) |
| 391 | { | 396 | { |
| 397 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 392 | struct fou *fout; | 398 | struct fou *fout; |
| 393 | 399 | ||
| 394 | spin_lock(&fou_lock); | 400 | mutex_lock(&fn->fou_lock); |
| 395 | list_for_each_entry(fout, &fou_list, list) { | 401 | list_for_each_entry(fout, &fn->fou_list, list) { |
| 396 | if (fou->port == fout->port) { | 402 | if (fou->port == fout->port) { |
| 397 | spin_unlock(&fou_lock); | 403 | mutex_unlock(&fn->fou_lock); |
| 398 | return -EALREADY; | 404 | return -EALREADY; |
| 399 | } | 405 | } |
| 400 | } | 406 | } |
| 401 | 407 | ||
| 402 | list_add(&fou->list, &fou_list); | 408 | list_add(&fou->list, &fn->fou_list); |
| 403 | spin_unlock(&fou_lock); | 409 | mutex_unlock(&fn->fou_lock); |
| 404 | 410 | ||
| 405 | return 0; | 411 | return 0; |
| 406 | } | 412 | } |
| @@ -410,14 +416,10 @@ static void fou_release(struct fou *fou) | |||
| 410 | struct socket *sock = fou->sock; | 416 | struct socket *sock = fou->sock; |
| 411 | struct sock *sk = sock->sk; | 417 | struct sock *sk = sock->sk; |
| 412 | 418 | ||
| 413 | udp_del_offload(&fou->udp_offloads); | 419 | if (sk->sk_family == AF_INET) |
| 414 | 420 | udp_del_offload(&fou->udp_offloads); | |
| 415 | list_del(&fou->list); | 421 | list_del(&fou->list); |
| 416 | 422 | udp_tunnel_sock_release(sock); | |
| 417 | /* Remove hooks into tunnel socket */ | ||
| 418 | sk->sk_user_data = NULL; | ||
| 419 | |||
| 420 | sock_release(sock); | ||
| 421 | 423 | ||
| 422 | kfree(fou); | 424 | kfree(fou); |
| 423 | } | 425 | } |
| @@ -447,10 +449,10 @@ static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) | |||
| 447 | static int fou_create(struct net *net, struct fou_cfg *cfg, | 449 | static int fou_create(struct net *net, struct fou_cfg *cfg, |
| 448 | struct socket **sockp) | 450 | struct socket **sockp) |
| 449 | { | 451 | { |
| 450 | struct fou *fou = NULL; | ||
| 451 | int err; | ||
| 452 | struct socket *sock = NULL; | 452 | struct socket *sock = NULL; |
| 453 | struct fou *fou = NULL; | ||
| 453 | struct sock *sk; | 454 | struct sock *sk; |
| 455 | int err; | ||
| 454 | 456 | ||
| 455 | /* Open UDP socket */ | 457 | /* Open UDP socket */ |
| 456 | err = udp_sock_create(net, &cfg->udp_config, &sock); | 458 | err = udp_sock_create(net, &cfg->udp_config, &sock); |
| @@ -486,6 +488,8 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 486 | goto error; | 488 | goto error; |
| 487 | } | 489 | } |
| 488 | 490 | ||
| 491 | fou->type = cfg->type; | ||
| 492 | |||
| 489 | udp_sk(sk)->encap_type = 1; | 493 | udp_sk(sk)->encap_type = 1; |
| 490 | udp_encap_enable(); | 494 | udp_encap_enable(); |
| 491 | 495 | ||
| @@ -502,7 +506,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 502 | goto error; | 506 | goto error; |
| 503 | } | 507 | } |
| 504 | 508 | ||
| 505 | err = fou_add_to_port_list(fou); | 509 | err = fou_add_to_port_list(net, fou); |
| 506 | if (err) | 510 | if (err) |
| 507 | goto error; | 511 | goto error; |
| 508 | 512 | ||
| @@ -514,27 +518,27 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 514 | error: | 518 | error: |
| 515 | kfree(fou); | 519 | kfree(fou); |
| 516 | if (sock) | 520 | if (sock) |
| 517 | sock_release(sock); | 521 | udp_tunnel_sock_release(sock); |
| 518 | 522 | ||
| 519 | return err; | 523 | return err; |
| 520 | } | 524 | } |
| 521 | 525 | ||
| 522 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) | 526 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) |
| 523 | { | 527 | { |
| 524 | struct fou *fou; | 528 | struct fou_net *fn = net_generic(net, fou_net_id); |
| 525 | u16 port = cfg->udp_config.local_udp_port; | 529 | __be16 port = cfg->udp_config.local_udp_port; |
| 526 | int err = -EINVAL; | 530 | int err = -EINVAL; |
| 531 | struct fou *fou; | ||
| 527 | 532 | ||
| 528 | spin_lock(&fou_lock); | 533 | mutex_lock(&fn->fou_lock); |
| 529 | list_for_each_entry(fou, &fou_list, list) { | 534 | list_for_each_entry(fou, &fn->fou_list, list) { |
| 530 | if (fou->port == port) { | 535 | if (fou->port == port) { |
| 531 | udp_del_offload(&fou->udp_offloads); | ||
| 532 | fou_release(fou); | 536 | fou_release(fou); |
| 533 | err = 0; | 537 | err = 0; |
| 534 | break; | 538 | break; |
| 535 | } | 539 | } |
| 536 | } | 540 | } |
| 537 | spin_unlock(&fou_lock); | 541 | mutex_unlock(&fn->fou_lock); |
| 538 | 542 | ||
| 539 | return err; | 543 | return err; |
| 540 | } | 544 | } |
| @@ -573,7 +577,7 @@ static int parse_nl_config(struct genl_info *info, | |||
| 573 | } | 577 | } |
| 574 | 578 | ||
| 575 | if (info->attrs[FOU_ATTR_PORT]) { | 579 | if (info->attrs[FOU_ATTR_PORT]) { |
| 576 | u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]); | 580 | __be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]); |
| 577 | 581 | ||
| 578 | cfg->udp_config.local_udp_port = port; | 582 | cfg->udp_config.local_udp_port = port; |
| 579 | } | 583 | } |
| @@ -592,6 +596,7 @@ static int parse_nl_config(struct genl_info *info, | |||
| 592 | 596 | ||
| 593 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | 597 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) |
| 594 | { | 598 | { |
| 599 | struct net *net = genl_info_net(info); | ||
| 595 | struct fou_cfg cfg; | 600 | struct fou_cfg cfg; |
| 596 | int err; | 601 | int err; |
| 597 | 602 | ||
| @@ -599,16 +604,120 @@ static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | |||
| 599 | if (err) | 604 | if (err) |
| 600 | return err; | 605 | return err; |
| 601 | 606 | ||
| 602 | return fou_create(&init_net, &cfg, NULL); | 607 | return fou_create(net, &cfg, NULL); |
