ipvs: add more mcast parameters for the sync daemon
[firefly-linux-kernel-4.4.55.git] / net / netfilter / ipvs / ip_vs_sync.c
index e68a43421479aa5016522165ee576ffd16df30e9..43f140950075f2a95d0be856482c92bfa9234ce7 100644 (file)
@@ -262,6 +262,11 @@ struct ip_vs_sync_mesg {
        /* ip_vs_sync_conn entries start here */
 };
 
+union ipvs_sockaddr {
+       struct sockaddr_in      in;
+       struct sockaddr_in6     in6;
+};
+
 struct ip_vs_sync_buff {
        struct list_head        list;
        unsigned long           firstuse;
@@ -1301,6 +1306,14 @@ static void set_mcast_loop(struct sock *sk, u_char loop)
        /* setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); */
        lock_sock(sk);
        inet->mc_loop = loop ? 1 : 0;
+#ifdef CONFIG_IP_VS_IPV6
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               /* IPV6_MULTICAST_LOOP */
+               np->mc_loop = loop ? 1 : 0;
+       }
+#endif
        release_sock(sk);
 }
 
@@ -1314,6 +1327,14 @@ static void set_mcast_ttl(struct sock *sk, u_char ttl)
        /* setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); */
        lock_sock(sk);
        inet->mc_ttl = ttl;
+#ifdef CONFIG_IP_VS_IPV6
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               /* IPV6_MULTICAST_HOPS */
+               np->mcast_hops = ttl;
+       }
+#endif
        release_sock(sk);
 }
 
@@ -1325,6 +1346,14 @@ static void set_mcast_pmtudisc(struct sock *sk, int val)
        /* setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, &val, sizeof(val)); */
        lock_sock(sk);
        inet->pmtudisc = val;
+#ifdef CONFIG_IP_VS_IPV6
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               /* IPV6_MTU_DISCOVER */
+               np->pmtudisc = val;
+       }
+#endif
        release_sock(sk);
 }
 
@@ -1347,6 +1376,14 @@ static int set_mcast_if(struct sock *sk, char *ifname)
        lock_sock(sk);
        inet->mc_index = dev->ifindex;
        /*  inet->mc_addr  = 0; */
+#ifdef CONFIG_IP_VS_IPV6
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
+
+               /* IPV6_MULTICAST_IF */
+               np->mcast_oif = dev->ifindex;
+       }
+#endif
        release_sock(sk);
 
        return 0;
@@ -1384,6 +1421,27 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname)
        return ret;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int join_mcast_group6(struct sock *sk, struct in6_addr *addr,
+                            char *ifname)
+{
+       struct net *net = sock_net(sk);
+       struct net_device *dev;
+       int ret;
+
+       dev = __dev_get_by_name(net, ifname);
+       if (!dev)
+               return -ENODEV;
+       if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)
+               return -EINVAL;
+
+       lock_sock(sk);
+       ret = ipv6_sock_mc_join(sk, dev->ifindex, addr);
+       release_sock(sk);
+
+       return ret;
+}
+#endif
 
 static int bind_mcastif_addr(struct socket *sock, char *ifname)
 {
@@ -1412,6 +1470,26 @@ static int bind_mcastif_addr(struct socket *sock, char *ifname)
        return sock->ops->bind(sock, (struct sockaddr*)&sin, sizeof(sin));
 }
 
+static void get_mcast_sockaddr(union ipvs_sockaddr *sa, int *salen,
+                              struct ipvs_sync_daemon_cfg *c, int id)
+{
+       if (AF_INET6 == c->mcast_af) {
+               sa->in6 = (struct sockaddr_in6) {
+                       .sin6_family = AF_INET6,
+                       .sin6_port = htons(c->mcast_port + id),
+               };
+               sa->in6.sin6_addr = c->mcast_group.in6;
+               *salen = sizeof(sa->in6);
+       } else {
+               sa->in = (struct sockaddr_in) {
+                       .sin_family = AF_INET,
+                       .sin_port = htons(c->mcast_port + id),
+               };
+               sa->in.sin_addr = c->mcast_group.in;
+               *salen = sizeof(sa->in);
+       }
+}
+
 /*
  *      Set up sending multicast socket over UDP
  */
@@ -1419,16 +1497,13 @@ static struct socket *make_send_sock(struct net *net, int id)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
        /* multicast addr */
-       struct sockaddr_in mcast_addr = {
-               .sin_family             = AF_INET,
-               .sin_port               = cpu_to_be16(IP_VS_SYNC_PORT + id),
-               .sin_addr.s_addr        = cpu_to_be32(IP_VS_SYNC_GROUP),
-       };
+       union ipvs_sockaddr mcast_addr;
        struct socket *sock;
-       int result;
+       int result, salen;
 
        /* First create a socket */
-       result = sock_create_kern(net, PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
+       result = sock_create_kern(net, ipvs->mcfg.mcast_af, SOCK_DGRAM,
+                                 IPPROTO_UDP, &sock);
        if (result < 0) {
                pr_err("Error during creation of socket; terminating\n");
                return ERR_PTR(result);
@@ -1440,21 +1515,25 @@ static struct socket *make_send_sock(struct net *net, int id)
        }
 
        set_mcast_loop(sock->sk, 0);
-       set_mcast_ttl(sock->sk, 1);
+       set_mcast_ttl(sock->sk, ipvs->mcfg.mcast_ttl);
        /* Allow fragmentation if MTU changes */
        set_mcast_pmtudisc(sock->sk, IP_PMTUDISC_DONT);
        result = sysctl_sync_sock_size(ipvs);
        if (result > 0)
                set_sock_size(sock->sk, 1, result);
 
-       result = bind_mcastif_addr(sock, ipvs->mcfg.mcast_ifn);
+       if (AF_INET == ipvs->mcfg.mcast_af)
+               result = bind_mcastif_addr(sock, ipvs->mcfg.mcast_ifn);
+       else
+               result = 0;
        if (result < 0) {
                pr_err("Error binding address of the mcast interface\n");
                goto error;
        }
 
+       get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->mcfg, id);
        result = sock->ops->connect(sock, (struct sockaddr *) &mcast_addr,
-                       sizeof(struct sockaddr), 0);
+                                   salen, 0);
        if (result < 0) {
                pr_err("Error connecting to the multicast addr\n");
                goto error;
@@ -1475,16 +1554,13 @@ static struct socket *make_receive_sock(struct net *net, int id)
 {
        struct netns_ipvs *ipvs = net_ipvs(net);
        /* multicast addr */
-       struct sockaddr_in mcast_addr = {
-               .sin_family             = AF_INET,
-               .sin_port               = cpu_to_be16(IP_VS_SYNC_PORT + id),
-               .sin_addr.s_addr        = cpu_to_be32(IP_VS_SYNC_GROUP),
-       };
+       union ipvs_sockaddr mcast_addr;
        struct socket *sock;
-       int result;
+       int result, salen;
 
        /* First create a socket */
-       result = sock_create_kern(net, PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
+       result = sock_create_kern(net, ipvs->bcfg.mcast_af, SOCK_DGRAM,
+                                 IPPROTO_UDP, &sock);
        if (result < 0) {
                pr_err("Error during creation of socket; terminating\n");
                return ERR_PTR(result);
@@ -1495,17 +1571,22 @@ static struct socket *make_receive_sock(struct net *net, int id)
        if (result > 0)
                set_sock_size(sock->sk, 0, result);
 
-       result = sock->ops->bind(sock, (struct sockaddr *) &mcast_addr,
-                       sizeof(struct sockaddr));
+       get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id);
+       result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen);
        if (result < 0) {
                pr_err("Error binding to the multicast addr\n");
                goto error;
        }
 
        /* join the multicast group */
-       result = join_mcast_group(sock->sk,
-                       (struct in_addr *) &mcast_addr.sin_addr,
-                       ipvs->bcfg.mcast_ifn);
+#ifdef CONFIG_IP_VS_IPV6
+       if (ipvs->bcfg.mcast_af == AF_INET6)
+               result = join_mcast_group6(sock->sk, &mcast_addr.in6.sin6_addr,
+                                          ipvs->bcfg.mcast_ifn);
+       else
+#endif
+               result = join_mcast_group(sock->sk, &mcast_addr.in.sin_addr,
+                                         ipvs->bcfg.mcast_ifn);
        if (result < 0) {
                pr_err("Error joining to the multicast group\n");
                goto error;
@@ -1721,12 +1802,23 @@ int start_sync_thread(struct net *net, struct ipvs_sync_daemon_cfg *c,
        } else
                count = ipvs->threads_mask + 1;
 
+       if (c->mcast_af == AF_UNSPEC) {
+               c->mcast_af = AF_INET;
+               c->mcast_group.ip = cpu_to_be32(IP_VS_SYNC_GROUP);
+       }
+       if (!c->mcast_port)
+               c->mcast_port = IP_VS_SYNC_PORT;
+       if (!c->mcast_ttl)
+               c->mcast_ttl = 1;
+
        dev = __dev_get_by_name(net, c->mcast_ifn);
        if (!dev) {
                pr_err("Unknown mcast interface: %s\n", c->mcast_ifn);
                return -ENODEV;
        }
-       hlen = sizeof(struct iphdr) + sizeof(struct udphdr);
+       hlen = (AF_INET6 == c->mcast_af) ?
+              sizeof(struct ipv6hdr) + sizeof(struct udphdr) :
+              sizeof(struct iphdr) + sizeof(struct udphdr);
        mtu = (state == IP_VS_STATE_BACKUP) ?
                  clamp(dev->mtu, 1500U, 65535U) : 1500U;
        min_mtu = (state == IP_VS_STATE_BACKUP) ? 1024 : 1;