These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / netfilter / ipvs / ip_vs_sync.c
index 19b9cce..803001a 100644 (file)
@@ -193,7 +193,7 @@ union ip_vs_sync_conn {
 #define IPVS_OPT_F_PARAM       (1 << (IPVS_OPT_PARAM-1))
 
 struct ip_vs_sync_thread_data {
-       struct net *net;
+       struct netns_ipvs *ipvs;
        struct socket *sock;
        char *buf;
        int id;
@@ -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;
@@ -320,26 +325,28 @@ sb_dequeue(struct netns_ipvs *ipvs, struct ipvs_master_sync_state *ms)
  * Create a new sync buffer for Version 1 proto.
  */
 static inline struct ip_vs_sync_buff *
-ip_vs_sync_buff_create(struct netns_ipvs *ipvs)
+ip_vs_sync_buff_create(struct netns_ipvs *ipvs, unsigned int len)
 {
        struct ip_vs_sync_buff *sb;
 
        if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC)))
                return NULL;
 
-       sb->mesg = kmalloc(ipvs->send_mesg_maxlen, GFP_ATOMIC);
+       len = max_t(unsigned int, len + sizeof(struct ip_vs_sync_mesg),
+                   ipvs->mcfg.sync_maxlen);
+       sb->mesg = kmalloc(len, GFP_ATOMIC);
        if (!sb->mesg) {
                kfree(sb);
                return NULL;
        }
        sb->mesg->reserved = 0;  /* old nr_conns i.e. must be zero now */
        sb->mesg->version = SYNC_PROTO_VER;
-       sb->mesg->syncid = ipvs->master_syncid;
+       sb->mesg->syncid = ipvs->mcfg.syncid;
        sb->mesg->size = htons(sizeof(struct ip_vs_sync_mesg));
        sb->mesg->nr_conns = 0;
        sb->mesg->spare = 0;
        sb->head = (unsigned char *)sb->mesg + sizeof(struct ip_vs_sync_mesg);
-       sb->end = (unsigned char *)sb->mesg + ipvs->send_mesg_maxlen;
+       sb->end = (unsigned char *)sb->mesg + len;
 
        sb->firstuse = jiffies;
        return sb;
@@ -402,7 +409,7 @@ select_master_thread_id(struct netns_ipvs *ipvs, struct ip_vs_conn *cp)
  * Create a new sync buffer for Version 0 proto.
  */
 static inline struct ip_vs_sync_buff *
-ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs)
+ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs, unsigned int len)
 {
        struct ip_vs_sync_buff *sb;
        struct ip_vs_sync_mesg_v0 *mesg;
@@ -410,17 +417,19 @@ ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs)
        if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC)))
                return NULL;
 
-       sb->mesg = kmalloc(ipvs->send_mesg_maxlen, GFP_ATOMIC);
+       len = max_t(unsigned int, len + sizeof(struct ip_vs_sync_mesg_v0),
+                   ipvs->mcfg.sync_maxlen);
+       sb->mesg = kmalloc(len, GFP_ATOMIC);
        if (!sb->mesg) {
                kfree(sb);
                return NULL;
        }
        mesg = (struct ip_vs_sync_mesg_v0 *)sb->mesg;
        mesg->nr_conns = 0;
-       mesg->syncid = ipvs->master_syncid;
+       mesg->syncid = ipvs->mcfg.syncid;
        mesg->size = htons(sizeof(struct ip_vs_sync_mesg_v0));
        sb->head = (unsigned char *)mesg + sizeof(struct ip_vs_sync_mesg_v0);
-       sb->end = (unsigned char *)mesg + ipvs->send_mesg_maxlen;
+       sb->end = (unsigned char *)mesg + len;
        sb->firstuse = jiffies;
        return sb;
 }
@@ -524,16 +533,15 @@ set:
  *      Version 0 , could be switched in by sys_ctl.
  *      Add an ip_vs_conn information into the current sync_buff.
  */
-static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
+static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
                               int pkts)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg_v0 *m;
        struct ip_vs_sync_conn_v0 *s;
        struct ip_vs_sync_buff *buff;
        struct ipvs_master_sync_state *ms;
        int id;
-       int len;
+       unsigned int len;
 
        if (unlikely(cp->af != AF_INET))
                return;
@@ -553,17 +561,19 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
        id = select_master_thread_id(ipvs, cp);
        ms = &ipvs->ms[id];
        buff = ms->sync_buff;
+       len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :
+               SIMPLE_CONN_SIZE;
        if (buff) {
                m = (struct ip_vs_sync_mesg_v0 *) buff->mesg;
                /* Send buffer if it is for v1 */
-               if (!m->nr_conns) {
+               if (buff->head + len > buff->end || !m->nr_conns) {
                        sb_queue_tail(ipvs, ms);
                        ms->sync_buff = NULL;
                        buff = NULL;
                }
        }
        if (!buff) {
-               buff = ip_vs_sync_buff_create_v0(ipvs);
+               buff = ip_vs_sync_buff_create_v0(ipvs, len);
                if (!buff) {
                        spin_unlock_bh(&ipvs->sync_buff_lock);
                        pr_err("ip_vs_sync_buff_create failed.\n");
@@ -572,8 +582,6 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
                ms->sync_buff = buff;
        }
 
-       len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :
-               SIMPLE_CONN_SIZE;
        m = (struct ip_vs_sync_mesg_v0 *) buff->mesg;
        s = (struct ip_vs_sync_conn_v0 *) buff->head;
 
@@ -597,12 +605,6 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
        m->nr_conns++;
        m->size = htons(ntohs(m->size) + len);
        buff->head += len;
-
-       /* check if there is a space for next one */
-       if (buff->head + FULL_CONN_SIZE > buff->end) {
-               sb_queue_tail(ipvs, ms);
-               ms->sync_buff = NULL;
-       }
        spin_unlock_bh(&ipvs->sync_buff_lock);
 
        /* synchronize its controller if it has */
@@ -612,7 +614,7 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
                        pkts = atomic_add_return(1, &cp->in_pkts);
                else
                        pkts = sysctl_sync_threshold(ipvs);
-               ip_vs_sync_conn(net, cp->control, pkts);
+               ip_vs_sync_conn(ipvs, cp, pkts);
        }
 }
 
@@ -621,9 +623,8 @@ static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
  *      Called by ip_vs_in.
  *      Sending Version 1 messages
  */
-void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts)
+void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg *m;
        union ip_vs_sync_conn *s;
        struct ip_vs_sync_buff *buff;
@@ -634,7 +635,7 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts)
 
        /* Handle old version of the protocol */
        if (sysctl_sync_ver(ipvs) == 0) {
-               ip_vs_sync_conn_v0(net, cp, pkts);
+               ip_vs_sync_conn_v0(ipvs, cp, pkts);
                return;
        }
        /* Do not sync ONE PACKET */
@@ -694,7 +695,7 @@ sloop:
        }
 
        if (!buff) {
-               buff = ip_vs_sync_buff_create(ipvs);
+               buff = ip_vs_sync_buff_create(ipvs, len);
                if (!buff) {
                        spin_unlock_bh(&ipvs->sync_buff_lock);
                        pr_err("ip_vs_sync_buff_create failed.\n");
@@ -781,21 +782,21 @@ control:
  *  fill_param used by version 1
  */
 static inline int
-ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
+ip_vs_conn_fill_param_sync(struct netns_ipvs *ipvs, int af, union ip_vs_sync_conn *sc,
                           struct ip_vs_conn_param *p,
                           __u8 *pe_data, unsigned int pe_data_len,
                           __u8 *pe_name, unsigned int pe_name_len)
 {
 #ifdef CONFIG_IP_VS_IPV6
        if (af == AF_INET6)
-               ip_vs_conn_fill_param(net, af, sc->v6.protocol,
+               ip_vs_conn_fill_param(ipvs, af, sc->v6.protocol,
                                      (const union nf_inet_addr *)&sc->v6.caddr,
                                      sc->v6.cport,
                                      (const union nf_inet_addr *)&sc->v6.vaddr,
                                      sc->v6.vport, p);
        else
 #endif
-               ip_vs_conn_fill_param(net, af, sc->v4.protocol,
+               ip_vs_conn_fill_param(ipvs, af, sc->v4.protocol,
                                      (const union nf_inet_addr *)&sc->v4.caddr,
                                      sc->v4.cport,
                                      (const union nf_inet_addr *)&sc->v4.vaddr,
@@ -834,7 +835,7 @@ ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
  *  Param: ...
  *         timeout is in sec.
  */
-static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
+static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *param,
                            unsigned int flags, unsigned int state,
                            unsigned int protocol, unsigned int type,
                            const union nf_inet_addr *daddr, __be16 dport,
@@ -843,7 +844,6 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
 {
        struct ip_vs_dest *dest;
        struct ip_vs_conn *cp;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
                cp = ip_vs_conn_in_get(param);
@@ -901,7 +901,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
                 * with synchronization, so we can make the assumption that
                 * the svc_af is the same as the dest_af
                 */
-               dest = ip_vs_find_dest(net, type, type, daddr, dport,
+               dest = ip_vs_find_dest(ipvs, type, type, daddr, dport,
                                       param->vaddr, param->vport, protocol,
                                       fwmark, flags);
 
@@ -938,7 +938,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
        } else {
                struct ip_vs_proto_data *pd;
 
-               pd = ip_vs_proto_data_get(net, protocol);
+               pd = ip_vs_proto_data_get(ipvs, protocol);
                if (!(flags & IP_VS_CONN_F_TEMPLATE) && pd && pd->timeout_table)
                        cp->timeout = pd->timeout_table[state];
                else
@@ -950,7 +950,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
 /*
  *  Process received multicast message for Version 0
  */
-static void ip_vs_process_message_v0(struct net *net, const char *buffer,
+static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer,
                                     const size_t buflen)
 {
        struct ip_vs_sync_mesg_v0 *m = (struct ip_vs_sync_mesg_v0 *)buffer;
@@ -1006,14 +1006,14 @@ static void ip_vs_process_message_v0(struct net *net, const char *buffer,
                        }
                }
 
-               ip_vs_conn_fill_param(net, AF_INET, s->protocol,
+               ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol,
                                      (const union nf_inet_addr *)&s->caddr,
                                      s->cport,
                                      (const union nf_inet_addr *)&s->vaddr,
                                      s->vport, &param);
 
                /* Send timeout as Zero */
-               ip_vs_proc_conn(net, &param, flags, state, s->protocol, AF_INET,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->protocol, AF_INET,
                                (union nf_inet_addr *)&s->daddr, s->dport,
                                0, 0, opt);
        }
@@ -1064,7 +1064,7 @@ static int ip_vs_proc_str(__u8 *p, unsigned int plen, unsigned int *data_len,
 /*
  *   Process a Version 1 sync. connection
  */
-static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end)
+static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *msg_end)
 {
        struct ip_vs_sync_conn_options opt;
        union  ip_vs_sync_conn *s;
@@ -1168,21 +1168,21 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end)
                        state = 0;
                }
        }
-       if (ip_vs_conn_fill_param_sync(net, af, s, &param, pe_data,
+       if (ip_vs_conn_fill_param_sync(ipvs, af, s, &param, pe_data,
                                       pe_data_len, pe_name, pe_name_len)) {
                retc = 50;
                goto out;
        }
        /* If only IPv4, just silent skip IPv6 */
        if (af == AF_INET)
-               ip_vs_proc_conn(net, &param, flags, state, s->v4.protocol, af,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->v4.protocol, af,
                                (union nf_inet_addr *)&s->v4.daddr, s->v4.dport,
                                ntohl(s->v4.timeout), ntohl(s->v4.fwmark),
                                (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
                                );
 #ifdef CONFIG_IP_VS_IPV6
        else
-               ip_vs_proc_conn(net, &param, flags, state, s->v6.protocol, af,
+               ip_vs_proc_conn(ipvs, &param, flags, state, s->v6.protocol, af,
                                (union nf_inet_addr *)&s->v6.daddr, s->v6.dport,
                                ntohl(s->v6.timeout), ntohl(s->v6.fwmark),
                                (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
@@ -1201,10 +1201,9 @@ out:
  *      ip_vs_conn entries.
  *      Handles Version 0 & 1
  */
-static void ip_vs_process_message(struct net *net, __u8 *buffer,
+static void ip_vs_process_message(struct netns_ipvs *ipvs, __u8 *buffer,
                                  const size_t buflen)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct ip_vs_sync_mesg *m2 = (struct ip_vs_sync_mesg *)buffer;
        __u8 *p, *msg_end;
        int i, nr_conns;
@@ -1219,7 +1218,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer,
                return;
        }
        /* SyncID sanity check */
-       if (ipvs->backup_syncid != 0 && m2->syncid != ipvs->backup_syncid) {
+       if (ipvs->bcfg.syncid != 0 && m2->syncid != ipvs->bcfg.syncid) {
                IP_VS_DBG(7, "BACKUP, Ignoring syncid = %d\n", m2->syncid);
                return;
        }
@@ -1254,7 +1253,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer,
                                return;
                        }
                        /* Process a single sync_conn */
-                       retc = ip_vs_proc_sync_conn(net, p, msg_end);
+                       retc = ip_vs_proc_sync_conn(ipvs, p, msg_end);
                        if (retc < 0) {
                                IP_VS_ERR_RL("BACKUP, Dropping buffer, Err: %d in decoding\n",
                                             retc);
@@ -1265,7 +1264,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer,
                }
        } else {
                /* Old type of message */
-               ip_vs_process_message_v0(net, buffer, buflen);
+               ip_vs_process_message_v0(ipvs, buffer, buflen);
                return;
        }
 }
@@ -1303,6 +1302,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);
 }
 
@@ -1316,6 +1323,33 @@ 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);
+}
+
+/* Control fragmentation of messages */
+static void set_mcast_pmtudisc(struct sock *sk, int val)
+{
+       struct inet_sock *inet = inet_sk(sk);
+
+       /* 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);
 }
 
@@ -1338,44 +1372,15 @@ static int set_mcast_if(struct sock *sk, char *ifname)
        lock_sock(sk);
        inet->mc_index = dev->ifindex;
        /*  inet->mc_addr  = 0; */
-       release_sock(sk);
-
-       return 0;
-}
-
+#ifdef CONFIG_IP_VS_IPV6
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *np = inet6_sk(sk);
 
-/*
- *     Set the maximum length of sync message according to the
- *     specified interface's MTU.
- */
-static int set_sync_mesg_maxlen(struct net *net, int sync_state)
-{
-       struct netns_ipvs *ipvs = net_ipvs(net);
-       struct net_device *dev;
-       int num;
-
-       if (sync_state == IP_VS_STATE_MASTER) {
-               dev = __dev_get_by_name(net, ipvs->master_mcast_ifn);
-               if (!dev)
-                       return -ENODEV;
-
-               num = (dev->mtu - sizeof(struct iphdr) -
-                      sizeof(struct udphdr) -
-                      SYNC_MESG_HEADER_LEN - 20) / SIMPLE_CONN_SIZE;
-               ipvs->send_mesg_maxlen = SYNC_MESG_HEADER_LEN +
-                       SIMPLE_CONN_SIZE * min(num, MAX_CONNS_PER_SYNCBUFF);
-               IP_VS_DBG(7, "setting the maximum length of sync sending "
-                         "message %d.\n", ipvs->send_mesg_maxlen);
-       } else if (sync_state == IP_VS_STATE_BACKUP) {
-               dev = __dev_get_by_name(net, ipvs->backup_mcast_ifn);
-               if (!dev)
-                       return -ENODEV;
-
-               ipvs->recv_mesg_maxlen = dev->mtu -
-                       sizeof(struct iphdr) - sizeof(struct udphdr);
-               IP_VS_DBG(7, "setting the maximum length of sync receiving "
-                         "message %d.\n", ipvs->recv_mesg_maxlen);
+               /* IPV6_MULTICAST_IF */
+               np->mcast_oif = dev->ifindex;
        }
+#endif
+       release_sock(sk);
 
        return 0;
 }
@@ -1405,15 +1410,34 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname)
 
        mreq.imr_ifindex = dev->ifindex;
 
-       rtnl_lock();
        lock_sock(sk);
        ret = ip_mc_join_group(sk, &mreq);
        release_sock(sk);
-       rtnl_unlock();
 
        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)
 {
@@ -1442,53 +1466,69 @@ 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
  */
-static struct socket *make_send_sock(struct net *net, int id)
+static struct socket *make_send_sock(struct netns_ipvs *ipvs, 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 move it to right name space later */
-       result = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
+       /* First create a socket */
+       result = sock_create_kern(ipvs->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);
        }
-       /*
-        * Kernel sockets that are a part of a namespace, should not
-        * hold a reference to a namespace in order to allow to stop it.
-        * After sk_change_net should be released using sk_release_kernel.
-        */
-       sk_change_net(sock->sk, net);
-       result = set_mcast_if(sock->sk, ipvs->master_mcast_ifn);
+       result = set_mcast_if(sock->sk, ipvs->mcfg.mcast_ifn);
        if (result < 0) {
                pr_err("Error setting outbound mcast interface\n");
                goto error;
        }
 
        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->master_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;
@@ -1497,7 +1537,7 @@ static struct socket *make_send_sock(struct net *net, int id)
        return sock;
 
 error:
-       sk_release_kernel(sock->sk);
+       sock_release(sock);
        return ERR_PTR(result);
 }
 
@@ -1505,47 +1545,42 @@ error:
 /*
  *      Set up receiving multicast socket over UDP
  */
-static struct socket *make_receive_sock(struct net *net, int id)
+static struct socket *make_receive_sock(struct netns_ipvs *ipvs, 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(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
+       result = sock_create_kern(ipvs->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);
        }
-       /*
-        * Kernel sockets that are a part of a namespace, should not
-        * hold a reference to a namespace in order to allow to stop it.
-        * After sk_change_net should be released using sk_release_kernel.
-        */
-       sk_change_net(sock->sk, net);
        /* it is equivalent to the REUSEADDR option in user-space */
        sock->sk->sk_reuse = SK_CAN_REUSE;
        result = sysctl_sync_sock_size(ipvs);
        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->backup_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;
@@ -1554,7 +1589,7 @@ static struct socket *make_receive_sock(struct net *net, int id)
        return sock;
 
 error:
-       sk_release_kernel(sock->sk);
+       sock_release(sock);
        return ERR_PTR(result);
 }
 
@@ -1646,14 +1681,14 @@ next_sync_buff(struct netns_ipvs *ipvs, struct ipvs_master_sync_state *ms)
 static int sync_thread_master(void *data)
 {
        struct ip_vs_sync_thread_data *tinfo = data;
-       struct netns_ipvs *ipvs = net_ipvs(tinfo->net);
+       struct netns_ipvs *ipvs = tinfo->ipvs;
        struct ipvs_master_sync_state *ms = &ipvs->ms[tinfo->id];
        struct sock *sk = tinfo->sock->sk;
        struct ip_vs_sync_buff *sb;
 
        pr_info("sync thread started: state = MASTER, mcast_ifn = %s, "
                "syncid = %d, id = %d\n",
-               ipvs->master_mcast_ifn, ipvs->master_syncid, tinfo->id);
+               ipvs->mcfg.mcast_ifn, ipvs->mcfg.syncid, tinfo->id);
 
        for (;;) {
                sb = next_sync_buff(ipvs, ms);
@@ -1692,7 +1727,7 @@ done:
                ip_vs_sync_buff_release(sb);
 
        /* release the sending multicast socket */
-       sk_release_kernel(tinfo->sock->sk);
+       sock_release(tinfo->sock);
        kfree(tinfo);
 
        return 0;
@@ -1702,12 +1737,12 @@ done:
 static int sync_thread_backup(void *data)
 {
        struct ip_vs_sync_thread_data *tinfo = data;
-       struct netns_ipvs *ipvs = net_ipvs(tinfo->net);
+       struct netns_ipvs *ipvs = tinfo->ipvs;
        int len;
 
        pr_info("sync thread started: state = BACKUP, mcast_ifn = %s, "
                "syncid = %d, id = %d\n",
-               ipvs->backup_mcast_ifn, ipvs->backup_syncid, tinfo->id);
+               ipvs->bcfg.mcast_ifn, ipvs->bcfg.syncid, tinfo->id);
 
        while (!kthread_should_stop()) {
                wait_event_interruptible(*sk_sleep(tinfo->sock->sk),
@@ -1717,19 +1752,19 @@ static int sync_thread_backup(void *data)
                /* do we have data now? */
                while (!skb_queue_empty(&(tinfo->sock->sk->sk_receive_queue))) {
                        len = ip_vs_receive(tinfo->sock, tinfo->buf,
-                                       ipvs->recv_mesg_maxlen);
+                                       ipvs->bcfg.sync_maxlen);
                        if (len <= 0) {
                                if (len != -EAGAIN)
                                        pr_err("receiving message error\n");
                                break;
                        }
 
-                       ip_vs_process_message(tinfo->net, tinfo->buf, len);
+                       ip_vs_process_message(ipvs, tinfo->buf, len);
                }
        }
 
        /* release the sending multicast socket */
-       sk_release_kernel(tinfo->sock->sk);
+       sock_release(tinfo->sock);
        kfree(tinfo->buf);
        kfree(tinfo);
 
@@ -1737,16 +1772,18 @@ static int sync_thread_backup(void *data)
 }
 
 
-int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
+int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
+                     int state)
 {
        struct ip_vs_sync_thread_data *tinfo;
        struct task_struct **array = NULL, *task;
        struct socket *sock;
-       struct netns_ipvs *ipvs = net_ipvs(net);
+       struct net_device *dev;
        char *name;
        int (*threadfn)(void *data);
-       int id, count;
+       int id, count, hlen;
        int result = -ENOMEM;
+       u16 mtu, min_mtu;
 
        IP_VS_DBG(7, "%s(): pid %d\n", __func__, task_pid_nr(current));
        IP_VS_DBG(7, "Each ip_vs_sync_conn entry needs %Zd bytes\n",
@@ -1758,22 +1795,46 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
        } 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(ipvs->net, c->mcast_ifn);
+       if (!dev) {
+               pr_err("Unknown mcast interface: %s\n", c->mcast_ifn);
+               return -ENODEV;
+       }
+       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;
+
+       if (c->sync_maxlen)
+               c->sync_maxlen = clamp_t(unsigned int,
+                                        c->sync_maxlen, min_mtu,
+                                        65535 - hlen);
+       else
+               c->sync_maxlen = mtu - hlen;
+
        if (state == IP_VS_STATE_MASTER) {
                if (ipvs->ms)
                        return -EEXIST;
 
-               strlcpy(ipvs->master_mcast_ifn, mcast_ifn,
-                       sizeof(ipvs->master_mcast_ifn));
-               ipvs->master_syncid = syncid;
+               ipvs->mcfg = *c;
                name = "ipvs-m:%d:%d";
                threadfn = sync_thread_master;
        } else if (state == IP_VS_STATE_BACKUP) {
                if (ipvs->backup_threads)
                        return -EEXIST;
 
-               strlcpy(ipvs->backup_mcast_ifn, mcast_ifn,
-                       sizeof(ipvs->backup_mcast_ifn));
-               ipvs->backup_syncid = syncid;
+               ipvs->bcfg = *c;
                name = "ipvs-b:%d:%d";
                threadfn = sync_thread_backup;
        } else {
@@ -1801,14 +1862,13 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
                if (!array)
                        goto out;
        }
-       set_sync_mesg_maxlen(net, state);
 
        tinfo = NULL;
        for (id = 0; id < count; id++) {
                if (state == IP_VS_STATE_MASTER)
-                       sock = make_send_sock(net, id);
+                       sock = make_send_sock(ipvs, id);
                else
-                       sock = make_receive_sock(net, id);
+                       sock = make_receive_sock(ipvs, id);
                if (IS_ERR(sock)) {
                        result = PTR_ERR(sock);
                        goto outtinfo;
@@ -1816,10 +1876,10 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
                tinfo = kmalloc(sizeof(*tinfo), GFP_KERNEL);
                if (!tinfo)
                        goto outsocket;
-               tinfo->net = net;
+               tinfo->ipvs = ipvs;
                tinfo->sock = sock;
                if (state == IP_VS_STATE_BACKUP) {
-                       tinfo->buf = kmalloc(ipvs->recv_mesg_maxlen,
+                       tinfo->buf = kmalloc(ipvs->bcfg.sync_maxlen,
                                             GFP_KERNEL);
                        if (!tinfo->buf)
                                goto outtinfo;
@@ -1854,11 +1914,11 @@ int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid)
        return 0;
 
 outsocket:
-       sk_release_kernel(sock->sk);
+       sock_release(sock);
 
 outtinfo:
        if (tinfo) {
-               sk_release_kernel(tinfo->sock->sk);
+               sock_release(tinfo->sock);
                kfree(tinfo->buf);
                kfree(tinfo);
        }
@@ -1880,9 +1940,8 @@ out:
 }
 
 
-int stop_sync_thread(struct net *net, int state)
+int stop_sync_thread(struct netns_ipvs *ipvs, int state)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
        struct task_struct **array;
        int id;
        int retc = -EINVAL;
@@ -1948,27 +2007,24 @@ int stop_sync_thread(struct net *net, int state)
 /*
  * Initialize data struct for each netns
  */
-int __net_init ip_vs_sync_net_init(struct net *net)
+int __net_init ip_vs_sync_net_init(struct netns_ipvs *ipvs)
 {
-       struct netns_ipvs *ipvs = net_ipvs(net);
-
        __mutex_init(&ipvs->sync_mutex, "ipvs->sync_mutex", &__ipvs_sync_key);
        spin_lock_init(&ipvs->sync_lock);
        spin_lock_init(&ipvs->sync_buff_lock);
        return 0;
 }
 
-void ip_vs_sync_net_cleanup(struct net *net)
+void ip_vs_sync_net_cleanup(struct netns_ipvs *ipvs)
 {
        int retc;
-       struct netns_ipvs *ipvs = net_ipvs(net);
 
        mutex_lock(&ipvs->sync_mutex);
-       retc = stop_sync_thread(net, IP_VS_STATE_MASTER);
+       retc = stop_sync_thread(ipvs, IP_VS_STATE_MASTER);
        if (retc && retc != -ESRCH)
                pr_err("Failed to stop Master Daemon\n");
 
-       retc = stop_sync_thread(net, IP_VS_STATE_BACKUP);
+       retc = stop_sync_thread(ipvs, IP_VS_STATE_BACKUP);
        if (retc && retc != -ESRCH)
                pr_err("Failed to stop Backup Daemon\n");
        mutex_unlock(&ipvs->sync_mutex);