Add ip_vs_schedule_v6() and ip_vs_sched_persist_v6() functions for scheduling IPv6 connections. Signed-off-by: Julius R. Volz <juliusv@xxxxxxxxxx> 2 files changed, 241 insertions(+), 0 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 6a58dff..8d28d98 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -991,6 +991,11 @@ extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); extern struct ip_vs_conn * ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb); +#ifdef CONFIG_IP_VS_IPV6 +extern struct ip_vs_conn * +ip_vs_schedule_v6(struct ip_vs_service *svc, const struct sk_buff *skb); +#endif + extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_protocol *pp); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 9a3d0df..ccd95ff 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -333,6 +333,180 @@ ip_vs_sched_persist(struct ip_vs_service *svc, return cp; } +#ifdef CONFIG_IP_VS_IPV6 +static struct ip_vs_conn * +ip_vs_sched_persist_v6(struct ip_vs_service *svc, + const struct sk_buff *skb, + __be16 ports[2]) +{ + struct ip_vs_conn *cp = NULL; + struct ipv6hdr *iph = ipv6_hdr(skb); + struct ip_vs_dest *dest; + struct ip_vs_conn *ct; + __be16 dport; /* destination port to forward */ + struct in6_addr snet; /* source network of the client, after masking */ + + /* Mask saddr with the netmask to adjust template granularity */ + ipv6_addr_prefix(&snet, &iph->saddr, svc->netmask); + + IP_VS_DBG(6, "p-schedule: src " NIP6_FMT ":%u dest " NIP6_FMT ":%u " + "mnet " NIP6_FMT "\n", + NIP6(iph->saddr), ntohs(ports[0]), + NIP6(iph->daddr), ntohs(ports[1]), + NIP6(snet)); + + /* + * As far as we know, FTP is a very complicated network protocol, and + * it uses control connection and data connections. For active FTP, + * FTP server initialize data connection to the client, its source port + * is often 20. For passive FTP, FTP server tells the clients the port + * that it passively listens to, and the client issues the data + * connection. In the tunneling or direct routing mode, the load + * balancer is on the client-to-server half of connection, the port + * number is unknown to the load balancer. So, a conn template like + * <caddr, 0, vaddr, 0, daddr, 0> is created for persistent FTP + * service, and a template like <caddr, 0, vaddr, vport, daddr, dport> + * is created for other persistent services. + */ + if (ports[1] == svc->port) { + /* Check if a template already exists */ + if (svc->port != FTPPORT) + ct = ip_vs_ct_in_get_v6(iph->nexthdr, &snet, 0, + &iph->daddr, ports[1]); + else + ct = ip_vs_ct_in_get_v6(iph->nexthdr, &snet, 0, + &iph->daddr, 0); + + if (!ct || !ip_vs_check_template(ct)) { + /* + * No template found or the dest of the connection + * template is not available. + */ + dest = svc->scheduler->schedule(svc, skb); + if (dest == NULL) { + IP_VS_DBG(1, "p-schedule: no dest found.\n"); + return NULL; + } + + /* + * Create a template like <protocol,caddr,0, + * vaddr,vport,daddr,dport> for non-ftp service, + * and <protocol,caddr,0,vaddr,0,daddr,0> + * for ftp service. + */ + if (svc->port != FTPPORT) + ct = ip_vs_conn_new_v6(iph->nexthdr, + &snet, 0, + &iph->daddr, + ports[1], + &dest->addr.v6, dest->port, + IP_VS_CONN_F_TEMPLATE, + dest); + else + ct = ip_vs_conn_new_v6(iph->nexthdr, + &snet, 0, + &iph->daddr, 0, + &dest->addr.v6, 0, + IP_VS_CONN_F_TEMPLATE, + dest); + if (ct == NULL) + return NULL; + + ct->timeout = svc->timeout; + } else { + /* set destination with the found template */ + dest = ct->dest; + } + dport = dest->port; + } else { + /* + * Note: persistent fwmark-based services and persistent + * port zero service are handled here. + * fwmark template: <IPPROTO_IP,caddr,0,fwmark,0,daddr,0> + * port zero template: <protocol,caddr,0,vaddr,0,daddr,0> + */ + if (svc->fwmark) { + struct in6_addr fwmark = { + .s6_addr32 = {0, 0, 0, htonl(svc->fwmark)} + }; + + ct = ip_vs_ct_in_get_v6(IPPROTO_IP, &snet, 0, + &fwmark, 0); + } else + ct = ip_vs_ct_in_get_v6(iph->nexthdr, &snet, 0, + &iph->daddr, 0); + + if (!ct || !ip_vs_check_template(ct)) { + /* + * If it is not persistent port zero, return NULL, + * otherwise create a connection template. + */ + if (svc->port) + return NULL; + + dest = svc->scheduler->schedule(svc, skb); + if (dest == NULL) { + IP_VS_DBG(1, "p-schedule: no dest found.\n"); + return NULL; + } + + /* + * Create a template according to the service + */ + if (svc->fwmark) { + struct in6_addr fwmark = { + .s6_addr32 = {0, 0, 0, htonl(svc->fwmark)} + }; + + ct = ip_vs_conn_new_v6(IPPROTO_IP, + &snet, 0, + &fwmark, 0, + &dest->addr.v6, 0, + IP_VS_CONN_F_TEMPLATE, + dest); + } + else + ct = ip_vs_conn_new_v6(iph->nexthdr, + &snet, 0, + &iph->daddr, 0, + &dest->addr.v6, 0, + IP_VS_CONN_F_TEMPLATE, + dest); + if (ct == NULL) + return NULL; + + ct->timeout = svc->timeout; + } else { + /* set destination with the found template */ + dest = ct->dest; + } + dport = ports[1]; + } + + /* + * Create a new connection according to the template + */ + cp = ip_vs_conn_new_v6(iph->nexthdr, + &iph->saddr, ports[0], + &iph->daddr, ports[1], + &dest->addr.v6, dport, + 0, + dest); + if (cp == NULL) { + ip_vs_conn_put(ct); + return NULL; + } + + /* + * Add its control + */ + ip_vs_control_add(cp, ct); + ip_vs_conn_put(ct); + + ip_vs_conn_stats(cp, svc); + return cp; +} +#endif /* * IPVS main scheduling function @@ -400,6 +574,68 @@ ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) return cp; } +#ifdef CONFIG_IP_VS_IPV6 +struct ip_vs_conn * +ip_vs_schedule_v6(struct ip_vs_service *svc, const struct sk_buff *skb) +{ + struct ip_vs_conn *cp = NULL; + struct ipv6hdr *iph = ipv6_hdr(skb); + struct ip_vs_dest *dest; + __be16 _ports[2], *pptr; + + pptr = skb_header_pointer(skb, sizeof(struct ipv6hdr), + sizeof(_ports), _ports); + if (pptr == NULL) + return NULL; + + /* + * Persistent service + */ + if (svc->flags & IP_VS_SVC_F_PERSISTENT) + return ip_vs_sched_persist_v6(svc, skb, pptr); + + /* + * Non-persistent service + */ + if (!svc->fwmark && pptr[1] != svc->port) { + if (!svc->port) + IP_VS_ERR("Schedule: port zero only supported " + "in persistent services, " + "check your ipvs configuration\n"); + return NULL; + } + + dest = svc->scheduler->schedule(svc, skb); + if (dest == NULL) { + IP_VS_DBG(1, "Schedule: no dest found.\n"); + return NULL; + } + + /* + * Create a connection entry. + */ + cp = ip_vs_conn_new_v6(iph->nexthdr, + &iph->saddr, pptr[0], + &iph->daddr, pptr[1], + &dest->addr.v6, dest->port?dest->port:pptr[1], + 0, + dest); + if (cp == NULL) + return NULL; + + IP_VS_DBG(6, "Schedule fwd:%c c:" NIP6_FMT ":%u v:" NIP6_FMT ":%u " + "d:" NIP6_FMT ":%u conn->flags:%X conn->refcnt:%d\n", + ip_vs_fwd_tag(cp), + NIP6(cp->caddr.v6), ntohs(cp->cport), + NIP6(cp->vaddr.v6), ntohs(cp->vport), + NIP6(cp->daddr.v6), ntohs(cp->dport), + cp->flags, atomic_read(&cp->refcnt)); + + ip_vs_conn_stats(cp, svc); + return cp; +} +#endif + /* * Pass or drop the packet. -- 1.5.3.6 -- 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