On Mon, Sep 01, 2008 at 02:56:02PM +0200, Julius Volz wrote: > Add extended internal versions of struct ip_vs_service_user and struct > ip_vs_dest_user (the originals can't be modified as they are part > of the old sockopt interface). Adjust ip_vs_ctl.c to work with the new > data structures and add some minor AF-awareness. > > Signed-off-by: Julius Volz <juliusv@xxxxxxxxxx> > > 2 files changed, 129 insertions(+), 48 deletions(-) > > diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h > index 45a7458..9657bd4 100644 > --- a/include/net/ip_vs.h > +++ b/include/net/ip_vs.h > @@ -400,6 +400,45 @@ struct ip_vs_conn { > > > /* > + * Extended internal versions of struct ip_vs_service_user and > + * ip_vs_dest_user for IPv6 support. > + * > + * We need these to conveniently pass around service and destination > + * options, but unfortunately, we also need to keep the old definitions to > + * maintain userspace backwards compatibility for the setsockopt interface. > + */ > +struct ip_vs_service_user_kern { > + /* virtual service addresses */ > + u_int16_t af; > + u_int16_t protocol; > + union nf_inet_addr addr; /* virtual ip address */ > + __be16 port; > + u_int32_t fwmark; /* firwall mark of service */ > + > + /* virtual service options */ > + char *sched_name; > + unsigned flags; /* virtual service flags */ > + unsigned timeout; /* persistent timeout in sec */ > + __be32 netmask; /* persistent netmask */ > +}; As this is an internal structure I beleive that u_int16_t and u_int32_t should be u16 and u32 respectively. > +struct ip_vs_dest_user_kern { > + /* destination server address */ > + union nf_inet_addr addr; > + __be16 port; > + > + /* real server options */ > + unsigned conn_flags; /* connection flags */ > + int weight; /* destination weight */ > + > + /* thresholds for active connections */ > + u_int32_t u_threshold; /* upper threshold */ > + u_int32_t l_threshold; /* lower threshold */ > +}; ditto > + > + > +/* > * The information about the virtual service offered to the net > * and the forwarding entries > */ > diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c > index 47644f3..52b3c1e 100644 > --- a/net/ipv4/ipvs/ip_vs_ctl.c > +++ b/net/ipv4/ipvs/ip_vs_ctl.c > @@ -707,7 +707,7 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) > */ > static void > __ip_vs_update_dest(struct ip_vs_service *svc, > - struct ip_vs_dest *dest, struct ip_vs_dest_user *udest) > + struct ip_vs_dest *dest, struct ip_vs_dest_user_kern *udest) > { > int conn_flags; > > @@ -716,7 +716,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, > conn_flags = udest->conn_flags | IP_VS_CONN_F_INACTIVE; > > /* check if local node and update the flags */ > - if (inet_addr_type(&init_net, udest->addr) == RTN_LOCAL) { > + if (inet_addr_type(&init_net, udest->addr.ip) == RTN_LOCAL) { > conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) > | IP_VS_CONN_F_LOCALNODE; > } > @@ -760,7 +760,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, > * Create a destination for the given service > */ > static int > -ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, > +ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, > struct ip_vs_dest **dest_p) > { > struct ip_vs_dest *dest; > @@ -768,7 +768,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, > > EnterFunction(2); > > - atype = inet_addr_type(&init_net, udest->addr); > + atype = inet_addr_type(&init_net, udest->addr.ip); > if (atype != RTN_LOCAL && atype != RTN_UNICAST) > return -EINVAL; > > @@ -778,11 +778,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, > return -ENOMEM; > } > > + dest->af = svc->af; > dest->protocol = svc->protocol; > - dest->vaddr.ip = svc->addr.ip; > + dest->vaddr = svc->addr; > dest->vport = svc->port; > dest->vfwmark = svc->fwmark; > - dest->addr.ip = udest->addr; > + ip_vs_addr_copy(svc->af, &dest->addr, &udest->addr); > dest->port = udest->port; > > atomic_set(&dest->activeconns, 0); > @@ -807,10 +808,10 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest, > * Add a destination into an existing service > */ > static int > -ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > +ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) > { > struct ip_vs_dest *dest; > - __be32 daddr = udest->addr; > + union nf_inet_addr daddr; > __be16 dport = udest->port; > int ret; > > @@ -827,10 +828,12 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > return -ERANGE; > } > > + ip_vs_addr_copy(svc->af, &daddr, &udest->addr); > + > /* > * Check if the dest already exists in the list > */ > - dest = ip_vs_lookup_dest(svc, daddr, dport); > + dest = ip_vs_lookup_dest(svc, daddr.ip, dport); > if (dest != NULL) { > IP_VS_DBG(1, "ip_vs_add_dest(): dest already exists\n"); > return -EEXIST; > @@ -840,7 +843,7 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > * Check if the dest already exists in the trash and > * is from the same service > */ > - dest = ip_vs_trash_get_dest(svc, daddr, dport); > + dest = ip_vs_trash_get_dest(svc, daddr.ip, dport); > if (dest != NULL) { > IP_VS_DBG(3, "Get destination %u.%u.%u.%u:%u from trash, " > "dest->refcnt=%d, service %u/%u.%u.%u.%u:%u\n", > @@ -915,10 +918,10 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > * Edit a destination in the given service > */ > static int > -ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > +ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) > { > struct ip_vs_dest *dest; > - __be32 daddr = udest->addr; > + union nf_inet_addr daddr; > __be16 dport = udest->port; > > EnterFunction(2); > @@ -934,10 +937,12 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest) > return -ERANGE; > } > > + ip_vs_addr_copy(svc->af, &daddr, &udest->addr); > + > /* > * Lookup the destination list > */ > - dest = ip_vs_lookup_dest(svc, daddr, dport); > + dest = ip_vs_lookup_dest(svc, daddr.ip, dport); > if (dest == NULL) { > IP_VS_DBG(1, "ip_vs_edit_dest(): dest doesn't exist\n"); > return -ENOENT; > @@ -1028,15 +1033,15 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc, > * Delete a destination server in the given service > */ > static int > -ip_vs_del_dest(struct ip_vs_service *svc,struct ip_vs_dest_user *udest) > +ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) > { > struct ip_vs_dest *dest; > - __be32 daddr = udest->addr; > __be16 dport = udest->port; > > EnterFunction(2); > > - dest = ip_vs_lookup_dest(svc, daddr, dport); > + dest = ip_vs_lookup_dest(svc, udest->addr.ip, dport); > + > if (dest == NULL) { > IP_VS_DBG(1, "ip_vs_del_dest(): destination not found!\n"); > return -ENOENT; > @@ -1071,7 +1076,8 @@ ip_vs_del_dest(struct ip_vs_service *svc,struct ip_vs_dest_user *udest) > * Add a service into the service hash table > */ > static int > -ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) > +ip_vs_add_service(struct ip_vs_service_user_kern *u, > + struct ip_vs_service **svc_p) > { > int ret = 0; > struct ip_vs_scheduler *sched = NULL; > @@ -1100,8 +1106,9 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) > atomic_set(&svc->usecnt, 1); > atomic_set(&svc->refcnt, 0); > > + svc->af = u->af; > svc->protocol = u->protocol; > - svc->addr.ip = u->addr; > + ip_vs_addr_copy(svc->af, &svc->addr, &u->addr); > svc->port = u->port; > svc->fwmark = u->fwmark; > svc->flags = u->flags; > @@ -1160,7 +1167,7 @@ ip_vs_add_service(struct ip_vs_service_user *u, struct ip_vs_service **svc_p) > * Edit a service and bind it with a new scheduler > */ > static int > -ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user *u) > +ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) > { > struct ip_vs_scheduler *sched, *old_sched; > int ret = 0; > @@ -1904,14 +1911,44 @@ static const unsigned char set_arglen[SET_CMDID(IP_VS_SO_SET_MAX)+1] = { > [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN, > }; > > +static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc, > + struct ip_vs_service_user *usvc_compat) > +{ > + usvc->af = AF_INET; > + usvc->protocol = usvc_compat->protocol; > + usvc->addr.ip = usvc_compat->addr; > + usvc->port = usvc_compat->port; > + usvc->fwmark = usvc_compat->fwmark; > + > + /* Deep copy of sched_name is not needed here */ > + usvc->sched_name = usvc_compat->sched_name; > + > + usvc->flags = usvc_compat->flags; > + usvc->timeout = usvc_compat->timeout; > + usvc->netmask = usvc_compat->netmask; > +} > + > +static void ip_vs_copy_udest_compat(struct ip_vs_dest_user_kern *udest, > + struct ip_vs_dest_user *udest_compat) > +{ > + udest->addr.ip = udest_compat->addr; > + udest->port = udest_compat->port; > + udest->conn_flags = udest_compat->conn_flags; > + udest->weight = udest_compat->weight; > + udest->u_threshold = udest_compat->u_threshold; > + udest->l_threshold = udest_compat->l_threshold; > +} > + > static int > do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) > { > int ret; > unsigned char arg[MAX_ARG_LEN]; > - struct ip_vs_service_user *usvc; > + struct ip_vs_service_user *usvc_compat; > + struct ip_vs_service_user_kern usvc; > struct ip_vs_service *svc; > - struct ip_vs_dest_user *udest; > + struct ip_vs_dest_user *udest_compat; > + struct ip_vs_dest_user_kern udest; > > if (!capable(CAP_NET_ADMIN)) > return -EPERM; > @@ -1951,35 +1988,40 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) > goto out_unlock; > } > > - usvc = (struct ip_vs_service_user *)arg; > - udest = (struct ip_vs_dest_user *)(usvc + 1); > + usvc_compat = (struct ip_vs_service_user *)arg; > + udest_compat = (struct ip_vs_dest_user *)(usvc_compat + 1); > + > + /* We only use the new structs internally, so copy userspace compat > + * structs to extended internal versions */ > + ip_vs_copy_usvc_compat(&usvc, usvc_compat); > + ip_vs_copy_udest_compat(&udest, udest_compat); > > if (cmd == IP_VS_SO_SET_ZERO) { > /* if no service address is set, zero counters in all */ > - if (!usvc->fwmark && !usvc->addr && !usvc->port) { > + if (!usvc.fwmark && !usvc.addr.ip && !usvc.port) { > ret = ip_vs_zero_all(); > goto out_unlock; > } > } > > /* Check for valid protocol: TCP or UDP, even for fwmark!=0 */ > - if (usvc->protocol!=IPPROTO_TCP && usvc->protocol!=IPPROTO_UDP) { > + if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) { > IP_VS_ERR("set_ctl: invalid protocol: %d %d.%d.%d.%d:%d %s\n", > - usvc->protocol, NIPQUAD(usvc->addr), > - ntohs(usvc->port), usvc->sched_name); > + usvc.protocol, NIPQUAD(usvc.addr.ip), > + ntohs(usvc.port), usvc.sched_name); > ret = -EFAULT; > goto out_unlock; > } > > /* Lookup the exact service by <protocol, addr, port> or fwmark */ > - if (usvc->fwmark == 0) > - svc = __ip_vs_service_get(usvc->protocol, > - usvc->addr, usvc->port); > + if (usvc.fwmark == 0) > + svc = __ip_vs_service_get(usvc.protocol, > + usvc.addr.ip, usvc.port); > else > - svc = __ip_vs_svc_fwm_get(usvc->fwmark); > + svc = __ip_vs_svc_fwm_get(usvc.fwmark); > > if (cmd != IP_VS_SO_SET_ADD > - && (svc == NULL || svc->protocol != usvc->protocol)) { > + && (svc == NULL || svc->protocol != usvc.protocol)) { > ret = -ESRCH; > goto out_unlock; > } > @@ -1989,10 +2031,10 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) > if (svc != NULL) > ret = -EEXIST; > else > - ret = ip_vs_add_service(usvc, &svc); > + ret = ip_vs_add_service(&usvc, &svc); > break; > case IP_VS_SO_SET_EDIT: > - ret = ip_vs_edit_service(svc, usvc); > + ret = ip_vs_edit_service(svc, &usvc); > break; > case IP_VS_SO_SET_DEL: > ret = ip_vs_del_service(svc); > @@ -2003,13 +2045,13 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) > ret = ip_vs_zero_service(svc); > break; > case IP_VS_SO_SET_ADDDEST: > - ret = ip_vs_add_dest(svc, udest); > + ret = ip_vs_add_dest(svc, &udest); > break; > case IP_VS_SO_SET_EDITDEST: > - ret = ip_vs_edit_dest(svc, udest); > + ret = ip_vs_edit_dest(svc, &udest); > break; > case IP_VS_SO_SET_DELDEST: > - ret = ip_vs_del_dest(svc, udest); > + ret = ip_vs_del_dest(svc, &udest); > break; > default: > ret = -EINVAL; > @@ -2516,7 +2558,7 @@ nla_put_failure: > return skb->len; > } > > -static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, > +static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, > struct nlattr *nla, int full_entry) > { > struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; > @@ -2536,6 +2578,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, > if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr)))) > return -EINVAL; > > + usvc->af = nla_get_u16(nla_af); > /* For now, only support IPv4 */ > if (nla_get_u16(nla_af) != AF_INET) > return -EAFNOSUPPORT; > @@ -2571,7 +2614,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, > if (usvc->fwmark) > svc = __ip_vs_svc_fwm_get(usvc->fwmark); > else > - svc = __ip_vs_service_get(usvc->protocol, usvc->addr, > + svc = __ip_vs_service_get(usvc->protocol, usvc->addr.ip, > usvc->port); > if (svc) { > usvc->flags = svc->flags; > @@ -2582,9 +2625,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, > /* set new flags from userland */ > usvc->flags = (usvc->flags & ~flags.mask) | > (flags.flags & flags.mask); > - > - strlcpy(usvc->sched_name, nla_data(nla_sched), > - sizeof(usvc->sched_name)); > + usvc->sched_name = nla_data(nla_sched); > usvc->timeout = nla_get_u32(nla_timeout); > usvc->netmask = nla_get_u32(nla_netmask); > } > @@ -2594,7 +2635,7 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, > > static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) > { > - struct ip_vs_service_user usvc; > + struct ip_vs_service_user_kern usvc; > int ret; > > ret = ip_vs_genl_parse_service(&usvc, nla, 0); > @@ -2604,7 +2645,7 @@ static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) > if (usvc.fwmark) > return __ip_vs_svc_fwm_get(usvc.fwmark); > else > - return __ip_vs_service_get(usvc.protocol, usvc.addr, > + return __ip_vs_service_get(usvc.protocol, usvc.addr.ip, > usvc.port); > } > > @@ -2704,7 +2745,7 @@ out_err: > return skb->len; > } > > -static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest, > +static int ip_vs_genl_parse_dest(struct ip_vs_dest_user_kern *udest, > struct nlattr *nla, int full_entry) > { > struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1]; > @@ -2860,8 +2901,8 @@ static int ip_vs_genl_set_config(struct nlattr **attrs) > static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) > { > struct ip_vs_service *svc = NULL; > - struct ip_vs_service_user usvc; > - struct ip_vs_dest_user udest; > + struct ip_vs_service_user_kern usvc; > + struct ip_vs_dest_user_kern udest; > int ret = 0, cmd; > int need_full_svc = 0, need_full_dest = 0; > > @@ -2913,7 +2954,8 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) > > /* Lookup the exact service by <protocol, addr, port> or fwmark */ > if (usvc.fwmark == 0) > - svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port); > + svc = __ip_vs_service_get(usvc.protocol, usvc.addr.ip, > + usvc.port); > else > svc = __ip_vs_svc_fwm_get(usvc.fwmark); > > -- > 1.5.4.5 -- To unsubscribe from this list: send the line "unsubscribe lvs-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html