/net/ipv4/ipvs/ip_vs_proto_udp.c
C | 421 lines | 302 code | 67 blank | 52 comment | 53 complexity | 5c53a5cce7ea4ebf5f41fa01e667a88a MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
- /*
- * ip_vs_proto_udp.c: UDP load balancing support for IPVS
- *
- * Version: $Id: ip_vs_proto_udp.c,v 1.3 2002/11/30 01:50:35 wensong Exp $
- *
- * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
- * Julian Anastasov <ja@ssi.bg>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Changes:
- *
- */
- #include <linux/kernel.h>
- #include <linux/netfilter_ipv4.h>
- #include <net/ip_vs.h>
- static struct ip_vs_conn *
- udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
- const struct iphdr *iph, unsigned int proto_off, int inverse)
- {
- struct ip_vs_conn *cp;
- __u16 ports[2];
- if (skb_copy_bits(skb, proto_off, ports, sizeof(ports)) < 0)
- return NULL;
- if (likely(!inverse)) {
- cp = ip_vs_conn_in_get(iph->protocol,
- iph->saddr, ports[0],
- iph->daddr, ports[1]);
- } else {
- cp = ip_vs_conn_in_get(iph->protocol,
- iph->daddr, ports[1],
- iph->saddr, ports[0]);
- }
- return cp;
- }
- static struct ip_vs_conn *
- udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
- const struct iphdr *iph, unsigned int proto_off, int inverse)
- {
- struct ip_vs_conn *cp;
- __u16 ports[2];
- if (skb_copy_bits(skb, skb->nh.iph->ihl*4, ports, sizeof(ports)) < 0)
- return NULL;
- if (likely(!inverse)) {
- cp = ip_vs_conn_out_get(iph->protocol,
- iph->saddr, ports[0],
- iph->daddr, ports[1]);
- } else {
- cp = ip_vs_conn_out_get(iph->protocol,
- iph->daddr, ports[1],
- iph->saddr, ports[0]);
- }
- return cp;
- }
- static int
- udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
- int *verdict, struct ip_vs_conn **cpp)
- {
- struct ip_vs_service *svc;
- struct udphdr udph;
- if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) {
- *verdict = NF_DROP;
- return 0;
- }
- if ((svc = ip_vs_service_get(skb->nfmark, skb->nh.iph->protocol,
- skb->nh.iph->daddr, udph.dest))) {
- if (ip_vs_todrop()) {
- /*
- * It seems that we are very loaded.
- * We have to drop this packet :(
- */
- ip_vs_service_put(svc);
- *verdict = NF_DROP;
- return 0;
- }
- /*
- * Let the virtual server select a real server for the
- * incoming connection, and create a connection entry.
- */
- *cpp = ip_vs_schedule(svc, skb);
- if (!*cpp) {
- *verdict = ip_vs_leave(svc, skb, pp);
- return 0;
- }
- ip_vs_service_put(svc);
- }
- return 1;
- }
- static inline void
- udp_fast_csum_update(struct udphdr *uhdr, u32 oldip, u32 newip,
- u16 oldport, u16 newport)
- {
- uhdr->check =
- ip_vs_check_diff(~oldip, newip,
- ip_vs_check_diff(oldport ^ 0xFFFF,
- newport, uhdr->check));
- if (!uhdr->check)
- uhdr->check = 0xFFFF;
- }
- static int
- udp_snat_handler(struct sk_buff **pskb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
- {
- struct udphdr *udph;
- unsigned int udphoff = (*pskb)->nh.iph->ihl * 4;
- /* csum_check requires unshared skb */
- if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph)))
- return 0;
- if (unlikely(cp->app != NULL)) {
- /* Some checks before mangling */
- if (pp->csum_check && !pp->csum_check(*pskb, pp))
- return 0;
- /*
- * Call application helper if needed
- */
- if (!ip_vs_app_pkt_out(cp, pskb))
- return 0;
- }
- udph = (void *)(*pskb)->nh.iph + udphoff;
- udph->source = cp->vport;
- /*
- * Adjust UDP checksums
- */
- if (!cp->app && (udph->check != 0)) {
- /* Only port and addr are changed, do fast csum update */
- udp_fast_csum_update(udph, cp->daddr, cp->vaddr,
- cp->dport, cp->vport);
- if ((*pskb)->ip_summed == CHECKSUM_HW)
- (*pskb)->ip_summed = CHECKSUM_NONE;
- } else {
- /* full checksum calculation */
- udph->check = 0;
- (*pskb)->csum = skb_checksum(*pskb, udphoff,
- (*pskb)->len - udphoff, 0);
- udph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr,
- (*pskb)->len - udphoff,
- cp->protocol,
- (*pskb)->csum);
- if (udph->check == 0)
- udph->check = 0xFFFF;
- IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n",
- pp->name, udph->check,
- (char*)&(udph->check) - (char*)udph);
- }
- return 1;
- }
- static int
- udp_dnat_handler(struct sk_buff **pskb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
- {
- struct udphdr *udph;
- unsigned int udphoff = (*pskb)->nh.iph->ihl * 4;
- /* csum_check requires unshared skb */
- if (!ip_vs_make_skb_writable(pskb, udphoff+sizeof(*udph)))
- return 0;
- if (unlikely(cp->app != NULL)) {
- /* Some checks before mangling */
- if (pp->csum_check && !pp->csum_check(*pskb, pp))
- return 0;
- /*
- * Attempt ip_vs_app call.
- * It will fix ip_vs_conn
- */
- if (!ip_vs_app_pkt_in(cp, pskb))
- return 0;
- }
- udph = (void *)(*pskb)->nh.iph + udphoff;
- udph->dest = cp->dport;
- /*
- * Adjust UDP checksums
- */
- if (!cp->app && (udph->check != 0)) {
- /* Only port and addr are changed, do fast csum update */
- udp_fast_csum_update(udph, cp->vaddr, cp->daddr,
- cp->vport, cp->dport);
- if ((*pskb)->ip_summed == CHECKSUM_HW)
- (*pskb)->ip_summed = CHECKSUM_NONE;
- } else {
- /* full checksum calculation */
- udph->check = 0;
- (*pskb)->csum = skb_checksum(*pskb, udphoff,
- (*pskb)->len - udphoff, 0);
- udph->check = csum_tcpudp_magic(cp->caddr, cp->daddr,
- (*pskb)->len - udphoff,
- cp->protocol,
- (*pskb)->csum);
- if (udph->check == 0)
- udph->check = 0xFFFF;
- (*pskb)->ip_summed = CHECKSUM_UNNECESSARY;
- }
- return 1;
- }
- static int
- udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
- {
- struct udphdr udph;
- unsigned int udphoff = skb->nh.iph->ihl*4;
- if (skb_copy_bits(skb, udphoff, &udph, sizeof(udph)) < 0)
- return 0;
- if (udph.check != 0) {
- switch (skb->ip_summed) {
- case CHECKSUM_NONE:
- skb->csum = skb_checksum(skb, udphoff,
- skb->len - udphoff, 0);
- case CHECKSUM_HW:
- if (csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr,
- skb->len - udphoff,
-