/* * Add an ip_vs_conn information into the current sync_buff. * Called by ip_vs_in. */ void ip_vs_sync_conn(struct ip_vs_conn *cp) { struct ip_vs_sync_mesg *m; struct ip_vs_sync_conn *s; int len; spin_lock(&curr_sb_lock); if (!curr_sb) { if (!(curr_sb=ip_vs_sync_buff_create())) { spin_unlock(&curr_sb_lock); IP_VS_ERR("ip_vs_sync_buff_create failed.\n"); return; } } len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE : SIMPLE_CONN_SIZE; m = curr_sb->mesg; s = (struct ip_vs_sync_conn *)curr_sb->head; /* copy members */ s->protocol = cp->protocol; s->cport = cp->cport; s->vport = cp->vport; s->dport = cp->dport; s->caddr = cp->caddr; s->vaddr = cp->vaddr; s->daddr = cp->daddr; s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED); s->state = htons(cp->state); if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { struct ip_vs_sync_conn_options *opt = (struct ip_vs_sync_conn_options *)&s[1]; memcpy(opt, &cp->in_seq, sizeof(*opt)); } m->nr_conns++; m->size += len; curr_sb->head += len; /* check if there is a space for next one */ if (curr_sb->head+FULL_CONN_SIZE > curr_sb->end) { sb_queue_tail(curr_sb); curr_sb = NULL; } spin_unlock(&curr_sb_lock); /* synchronize its controller if it has */ if (cp->control) ip_vs_sync_conn(cp->control); }
/* * Check if it's for virtual services, look it up, * and send it on its way... */ static unsigned int ip_vs_in(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *skb = *pskb; struct iphdr *iph; struct ip_vs_protocol *pp; struct ip_vs_conn *cp; int ret, restart; int ihl; /* * Big tappo: only PACKET_HOST (neither loopback nor mcasts) * ... don't know why 1st test DOES NOT include 2nd (?) */ if (unlikely(skb->pkt_type != PACKET_HOST || skb->dev == &loopback_dev || skb->sk)) { IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n", skb->pkt_type, ip_hdr(skb)->protocol, NIPQUAD(ip_hdr(skb)->daddr)); return NF_ACCEPT; } iph = ip_hdr(skb); if (unlikely(iph->protocol == IPPROTO_ICMP)) { int related, verdict = ip_vs_in_icmp(pskb, &related, hooknum); if (related) return verdict; skb = *pskb; iph = ip_hdr(skb); } /* Protocol supported? */ pp = ip_vs_proto_get(iph->protocol); if (unlikely(!pp)) return NF_ACCEPT; ihl = iph->ihl << 2; /* * Check if the packet belongs to an existing connection entry */ cp = pp->conn_in_get(skb, pp, iph, ihl, 0); if (unlikely(!cp)) { int v; if (!pp->conn_schedule(skb, pp, &v, &cp)) return v; } if (unlikely(!cp)) { /* sorry, all this trouble for a no-hit :) */ IP_VS_DBG_PKT(12, pp, skb, 0, "packet continues traversal as normal"); return NF_ACCEPT; } IP_VS_DBG_PKT(11, pp, skb, 0, "Incoming packet"); /* Check the server status */ if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { /* the destination server is not available */ if (sysctl_ip_vs_expire_nodest_conn) { /* try to expire the connection immediately */ ip_vs_conn_expire_now(cp); } /* don't restart its timer, and silently drop the packet. */ __ip_vs_conn_put(cp); return NF_DROP; } ip_vs_in_stats(cp, skb); restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pp); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp); /* do not touch skb anymore */ else { IP_VS_DBG_RL("warning: packet_xmit is null"); ret = NF_ACCEPT; } /* increase its packet counter and check if it is needed to be synchronized */ atomic_inc(&cp->in_pkts); if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && (cp->protocol != IPPROTO_TCP || cp->state == IP_VS_TCP_S_ESTABLISHED) && (atomic_read(&cp->in_pkts) % sysctl_ip_vs_sync_threshold[1] == sysctl_ip_vs_sync_threshold[0])) ip_vs_sync_conn(cp); ip_vs_conn_put(cp); return ret; }
/* * Check if it's for virtual services, look it up, * and send it on its way... */ static unsigned int ip_vs_in(unsigned int hooknum, struct sk_buff **skb_p, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->nh.iph; union ip_vs_tphdr h; struct ip_vs_conn *cp; struct ip_vs_service *svc; int ihl; int ret; /* * Big tappo: only PACKET_HOST (nor loopback neither mcasts) * ... don't know why 1st test DOES NOT include 2nd (?) */ if (skb->pkt_type != PACKET_HOST || skb->dev == &loopback_dev) { IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n", skb->pkt_type, iph->protocol, NIPQUAD(iph->daddr)); return NF_ACCEPT; } if (iph->protocol == IPPROTO_ICMP) return ip_vs_in_icmp(skb_p); /* let it go if other IP protocols */ if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) return NF_ACCEPT; /* make sure that protocol header available in skb data area, note that skb data area may be reallocated. */ ihl = iph->ihl << 2; if (ip_vs_header_check(skb, iph->protocol, ihl) == -1) return NF_DROP; iph = skb->nh.iph; h.raw = (char*) iph + ihl; /* * Check if the packet belongs to an existing connection entry */ cp = ip_vs_conn_in_get(iph->protocol, iph->saddr, h.portp[0], iph->daddr, h.portp[1]); if (!cp && (h.th->syn || (iph->protocol!=IPPROTO_TCP)) && (svc = ip_vs_service_get(skb->nfmark, iph->protocol, iph->daddr, h.portp[1]))) { if (ip_vs_todrop()) { /* * It seems that we are very loaded. * We have to drop this packet :( */ ip_vs_service_put(svc); return NF_DROP; } /* * Let the virtual server select a real server for the * incoming connection, and create a connection entry. */ cp = ip_vs_schedule(svc, iph); if (!cp) return ip_vs_leave(svc, skb); ip_vs_conn_stats(cp, svc); ip_vs_service_put(svc); } if (!cp) { /* sorry, all this trouble for a no-hit :) */ IP_VS_DBG(12, "packet for %s %d.%d.%d.%d:%d continue " "traversal as normal.\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->daddr), ntohs(h.portp[1])); return NF_ACCEPT; } IP_VS_DBG(11, "Incoming %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->saddr), ntohs(h.portp[0]), NIPQUAD(iph->daddr), ntohs(h.portp[1])); /* Check the server status */ if (cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { /* the destination server is not available */ if (sysctl_ip_vs_expire_nodest_conn) { /* try to expire the connection immediately */ ip_vs_conn_expire_now(cp); } else { /* don't restart its timer, and silently drop the packet. */ __ip_vs_conn_put(cp); } return NF_DROP; } ip_vs_in_stats(cp, skb); ip_vs_set_state(cp, VS_STATE_INPUT, iph, h.portp); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp); else { IP_VS_DBG_RL("warning: packet_xmit is null"); ret = NF_ACCEPT; } /* increase its packet counter and check if it is needed to be synchronized */ atomic_inc(&cp->in_pkts); if (ip_vs_sync_state & IP_VS_STATE_MASTER && (cp->protocol != IPPROTO_TCP || cp->state == IP_VS_S_ESTABLISHED) && (atomic_read(&cp->in_pkts) % 50 == sysctl_ip_vs_sync_threshold)) ip_vs_sync_conn(cp); ip_vs_conn_put(cp); return ret; }