|
@@ -21,6 +21,7 @@ struct fou {
|
|
|
u8 protocol;
|
|
u8 protocol;
|
|
|
u8 flags;
|
|
u8 flags;
|
|
|
__be16 port;
|
|
__be16 port;
|
|
|
|
|
+ u8 family;
|
|
|
u16 type;
|
|
u16 type;
|
|
|
struct list_head list;
|
|
struct list_head list;
|
|
|
struct rcu_head rcu;
|
|
struct rcu_head rcu;
|
|
@@ -47,14 +48,17 @@ static inline struct fou *fou_from_sock(struct sock *sk)
|
|
|
return sk->sk_user_data;
|
|
return sk->sk_user_data;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int fou_recv_pull(struct sk_buff *skb, size_t len)
|
|
|
|
|
|
|
+static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
|
|
|
{
|
|
{
|
|
|
- struct iphdr *iph = ip_hdr(skb);
|
|
|
|
|
-
|
|
|
|
|
/* Remove 'len' bytes from the packet (UDP header and
|
|
/* Remove 'len' bytes from the packet (UDP header and
|
|
|
* FOU header if present).
|
|
* FOU header if present).
|
|
|
*/
|
|
*/
|
|
|
- iph->tot_len = htons(ntohs(iph->tot_len) - len);
|
|
|
|
|
|
|
+ if (fou->family == AF_INET)
|
|
|
|
|
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
|
|
|
|
|
+ else
|
|
|
|
|
+ ipv6_hdr(skb)->payload_len =
|
|
|
|
|
+ htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
|
|
|
|
|
+
|
|
|
__skb_pull(skb, len);
|
|
__skb_pull(skb, len);
|
|
|
skb_postpull_rcsum(skb, udp_hdr(skb), len);
|
|
skb_postpull_rcsum(skb, udp_hdr(skb), len);
|
|
|
skb_reset_transport_header(skb);
|
|
skb_reset_transport_header(skb);
|
|
@@ -68,7 +72,7 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
|
if (!fou)
|
|
if (!fou)
|
|
|
return 1;
|
|
return 1;
|
|
|
|
|
|
|
|
- if (fou_recv_pull(skb, sizeof(struct udphdr)))
|
|
|
|
|
|
|
+ if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
|
|
|
goto drop;
|
|
goto drop;
|
|
|
|
|
|
|
|
return -fou->protocol;
|
|
return -fou->protocol;
|
|
@@ -141,7 +145,11 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
|
|
|
|
|
|
hdrlen = sizeof(struct guehdr) + optlen;
|
|
hdrlen = sizeof(struct guehdr) + optlen;
|
|
|
|
|
|
|
|
- ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
|
|
|
|
|
|
|
+ if (fou->family == AF_INET)
|
|
|
|
|
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
|
|
|
|
|
+ else
|
|
|
|
|
+ ipv6_hdr(skb)->payload_len =
|
|
|
|
|
+ htons(ntohs(ipv6_hdr(skb)->payload_len) - len);
|
|
|
|
|
|
|
|
/* Pull csum through the guehdr now . This can be used if
|
|
/* Pull csum through the guehdr now . This can be used if
|
|
|
* there is a remote checksum offload.
|
|
* there is a remote checksum offload.
|
|
@@ -426,7 +434,8 @@ static int fou_add_to_port_list(struct net *net, struct fou *fou)
|
|
|
|
|
|
|
|
mutex_lock(&fn->fou_lock);
|
|
mutex_lock(&fn->fou_lock);
|
|
|
list_for_each_entry(fout, &fn->fou_list, list) {
|
|
list_for_each_entry(fout, &fn->fou_list, list) {
|
|
|
- if (fou->port == fout->port) {
|
|
|
|
|
|
|
+ if (fou->port == fout->port &&
|
|
|
|
|
+ fou->family == fout->family) {
|
|
|
mutex_unlock(&fn->fou_lock);
|
|
mutex_unlock(&fn->fou_lock);
|
|
|
return -EALREADY;
|
|
return -EALREADY;
|
|
|
}
|
|
}
|
|
@@ -471,8 +480,9 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
|
|
|
|
|
|
|
|
sk = sock->sk;
|
|
sk = sock->sk;
|
|
|
|
|
|
|
|
- fou->flags = cfg->flags;
|
|
|
|
|
fou->port = cfg->udp_config.local_udp_port;
|
|
fou->port = cfg->udp_config.local_udp_port;
|
|
|
|
|
+ fou->family = cfg->udp_config.family;
|
|
|
|
|
+ fou->flags = cfg->flags;
|
|
|
fou->type = cfg->type;
|
|
fou->type = cfg->type;
|
|
|
fou->sock = sock;
|
|
fou->sock = sock;
|
|
|
|
|
|
|
@@ -524,12 +534,13 @@ static int fou_destroy(struct net *net, struct fou_cfg *cfg)
|
|
|
{
|
|
{
|
|
|
struct fou_net *fn = net_generic(net, fou_net_id);
|
|
struct fou_net *fn = net_generic(net, fou_net_id);
|
|
|
__be16 port = cfg->udp_config.local_udp_port;
|
|
__be16 port = cfg->udp_config.local_udp_port;
|
|
|
|
|
+ u8 family = cfg->udp_config.family;
|
|
|
int err = -EINVAL;
|
|
int err = -EINVAL;
|
|
|
struct fou *fou;
|
|
struct fou *fou;
|
|
|
|
|
|
|
|
mutex_lock(&fn->fou_lock);
|
|
mutex_lock(&fn->fou_lock);
|
|
|
list_for_each_entry(fou, &fn->fou_list, list) {
|
|
list_for_each_entry(fou, &fn->fou_list, list) {
|
|
|
- if (fou->port == port) {
|
|
|
|
|
|
|
+ if (fou->port == port && fou->family == family) {
|
|
|
fou_release(fou);
|
|
fou_release(fou);
|
|
|
err = 0;
|
|
err = 0;
|
|
|
break;
|
|
break;
|
|
@@ -567,8 +578,15 @@ static int parse_nl_config(struct genl_info *info,
|
|
|
if (info->attrs[FOU_ATTR_AF]) {
|
|
if (info->attrs[FOU_ATTR_AF]) {
|
|
|
u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
|
|
u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
|
|
|
|
|
|
|
|
- if (family != AF_INET)
|
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
|
+ switch (family) {
|
|
|
|
|
+ case AF_INET:
|
|
|
|
|
+ break;
|
|
|
|
|
+ case AF_INET6:
|
|
|
|
|
+ cfg->udp_config.ipv6_v6only = 1;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return -EAFNOSUPPORT;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
cfg->udp_config.family = family;
|
|
cfg->udp_config.family = family;
|
|
|
}
|
|
}
|
|
@@ -659,6 +677,7 @@ static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
|
|
|
struct fou_cfg cfg;
|
|
struct fou_cfg cfg;
|
|
|
struct fou *fout;
|
|
struct fou *fout;
|
|
|
__be16 port;
|
|
__be16 port;
|
|
|
|
|
+ u8 family;
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parse_nl_config(info, &cfg);
|
|
ret = parse_nl_config(info, &cfg);
|
|
@@ -668,6 +687,10 @@ static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (port == 0)
|
|
if (port == 0)
|
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
+ family = cfg.udp_config.family;
|
|
|
|
|
+ if (family != AF_INET && family != AF_INET6)
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
if (!msg)
|
|
if (!msg)
|
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
@@ -675,7 +698,7 @@ static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
|
|
|
ret = -ESRCH;
|
|
ret = -ESRCH;
|
|
|
mutex_lock(&fn->fou_lock);
|
|
mutex_lock(&fn->fou_lock);
|
|
|
list_for_each_entry(fout, &fn->fou_list, list) {
|
|
list_for_each_entry(fout, &fn->fou_list, list) {
|
|
|
- if (port == fout->port) {
|
|
|
|
|
|
|
+ if (port == fout->port && family == fout->family) {
|
|
|
ret = fou_dump_info(fout, info->snd_portid,
|
|
ret = fou_dump_info(fout, info->snd_portid,
|
|
|
info->snd_seq, 0, msg,
|
|
info->snd_seq, 0, msg,
|
|
|
info->genlhdr->cmd);
|
|
info->genlhdr->cmd);
|