📄 ip_vs_core.c
字号:
ret = cp->packet_xmit(skb, cp); atomic_inc(&cp->in_pkts); ip_vs_conn_put(cp); return ret; } /* * When the virtual ftp service is presented, packets destined * for other services on the VIP may get here (except services * listed in the ipvs table), pass the packets, because it is * not ipvs job to decide to drop the packets. */ if ((svc->port == FTPPORT) && (portp[1] != FTPPORT)) { ip_vs_service_put(svc); return NF_ACCEPT; } ip_vs_service_put(svc); /* * Notify the client that the destination is unreachable, and * release the socket buffer. * Since it is in IP layer, the TCP socket is not actually * created, the TCP RST packet cannot be sent, instead that * ICMP_PORT_UNREACH is sent here no matter it is TCP/UDP. --WZ */ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); kfree_skb(skb); return NF_STOLEN;}/* * It is hooked before NF_IP_PRI_NAT_SRC at the NF_IP_POST_ROUTING * chain, and is used for VS/NAT. * It detects packets for VS/NAT connections and sends the packets * immediately. This can avoid that iptable_nat mangles the packets * for VS/NAT. */static unsigned int ip_vs_post_routing(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; if (!(skb->nfcache & NFC_IPVS_PROPERTY)) return NF_ACCEPT; /* The packet was sent from IPVS, exit this chain */ (*okfn)(skb); return NF_STOLEN;}/* * Handle ICMP messages in the inside-to-outside direction (outgoing). * Find any that might be relevant, check against existing connections, * forward to the right destination host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded. * (Only used in VS/NAT) */static int ip_vs_out_icmp(struct sk_buff **skb_p){ struct sk_buff *skb = *skb_p; struct iphdr *iph; struct icmphdr *icmph; struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ unsigned short ihl; unsigned short len; unsigned short clen, csize; struct ip_vs_conn *cp; /* reassemble IP fragments, but will it happen in ICMP packets?? */ if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb); if (!skb) return NF_STOLEN; *skb_p = skb; } if (skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) return NF_DROP; ip_send_check(skb->nh.iph); } iph = skb->nh.iph; ihl = iph->ihl << 2; icmph = (struct icmphdr *)((char *)iph + ihl); len = ntohs(iph->tot_len) - ihl; if (len < sizeof(struct icmphdr)) return NF_DROP; IP_VS_DBG(12, "outgoing ICMP (%d,%d) %u.%u.%u.%u->%u.%u.%u.%u\n", icmph->type, ntohs(icmp_id(icmph)), NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); /* * Work through seeing if this is for us. * These checks are supposed to be in an order that means easy * things are checked first to speed up processing.... however * this means that some packets will manage to get a long way * down this stack and then be rejected, but that's life. */ if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_SOURCE_QUENCH) && (icmph->type != ICMP_TIME_EXCEEDED)) return NF_ACCEPT; /* Now find the contained IP header */ clen = len - sizeof(struct icmphdr); if (clen < sizeof(struct iphdr)) return NF_DROP; ciph = (struct iphdr *) (icmph + 1); csize = ciph->ihl << 2; if (clen < csize) return NF_DROP; /* We are only interested ICMPs generated from TCP or UDP packets */ if (ciph->protocol != IPPROTO_UDP && ciph->protocol != IPPROTO_TCP) return NF_ACCEPT; /* Skip non-first embedded TCP/UDP fragments */ if (ciph->frag_off & __constant_htons(IP_OFFSET)) return NF_ACCEPT; /* We need at least TCP/UDP ports here */ if (clen < csize + sizeof(struct udphdr)) return NF_DROP; /* * Find the ports involved - this packet was * incoming so the ports are right way round * (but reversed relative to outer IP header!) */ pptr = (__u16 *)&(((char *)ciph)[csize]); /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ IP_VS_DBG(1, "forward ICMP: failed checksum from %d.%d.%d.%d!\n", NIPQUAD(iph->saddr)); return NF_DROP; } IP_VS_DBG(11, "Handling outgoing ICMP for " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); /* ciph content is actually <protocol, caddr, cport, daddr, dport> */ cp = ip_vs_conn_out_get(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); if (!cp) return NF_ACCEPT; if (IP_VS_FWD_METHOD(cp) != 0) { IP_VS_ERR("shouldn't reach here, because the box is on the" "half connection in the tun/dr module.\n"); } /* Now we do real damage to this packet...! */ /* First change the source IP address, and recalc checksum */ iph->saddr = cp->vaddr; ip_send_check(iph); /* Now change the *dest* address in the contained IP */ ciph->daddr = cp->vaddr; ip_send_check(ciph); /* the TCP/UDP dest port - cannot redo check */ pptr[1] = cp->vport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); skb->ip_summed = CHECKSUM_UNNECESSARY; /* do the statistics and put it back */ ip_vs_out_stats(cp, skb); ip_vs_conn_put(cp); IP_VS_DBG(11, "Forwarding correct outgoing ICMP to " "%u.%u.%u.%u:%d -> %u.%u.%u.%u:%d\n", NIPQUAD(ciph->saddr), ntohs(pptr[0]), NIPQUAD(ciph->daddr), ntohs(pptr[1])); skb->nfcache |= NFC_IPVS_PROPERTY; return NF_ACCEPT;}/* * It is hooked at the NF_IP_FORWARD chain, used only for VS/NAT. * Check if outgoing packet belongs to the established ip_vs_conn, * rewrite addresses of the packet and send it on its way... */static unsigned int ip_vs_out(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; union ip_vs_tphdr h; struct ip_vs_conn *cp; int size; int ihl; EnterFunction(11); if (skb->nfcache & NFC_IPVS_PROPERTY) return NF_ACCEPT; iph = skb->nh.iph; if (iph->protocol == IPPROTO_ICMP) return ip_vs_out_icmp(skb_p); /* let it go if other IP protocols */ if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) return NF_ACCEPT; /* reassemble IP fragments */ if (iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { skb = ip_defrag(skb); if (!skb) return NF_STOLEN; iph = skb->nh.iph; *skb_p = skb; } /* 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 old entry */ cp = ip_vs_conn_out_get(iph->protocol, iph->saddr, h.portp[0], iph->daddr, h.portp[1]); if (!cp) { if (sysctl_ip_vs_nat_icmp_send && ip_vs_lookup_real_service(iph->protocol, iph->saddr, h.portp[0])) { /* * Notify the real server: there is no existing * entry if it is not RST packet or not TCP packet. */ if (!h.th->rst || iph->protocol != IPPROTO_TCP) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); kfree_skb(skb); return NF_STOLEN; } } 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])); if (skb_is_nonlinear(skb)) ip_send_check(iph); return NF_ACCEPT; } /* * If it has ip_vs_app helper, the helper may change the payload, * so it needs full checksum checking and checksum calculation. * If not, only the header (addr/port) is changed, so it is fast * to do incremental checksum update, and let the destination host * do final checksum checking. */ if (cp->app && skb_is_nonlinear(skb)) { if (skb_linearize(skb, GFP_ATOMIC) != 0) { ip_vs_conn_put(cp); return NF_DROP; } iph = skb->nh.iph; h.raw = (char*) iph + ihl; } size = skb->len - ihl; IP_VS_DBG(11, "O-pkt: %s size=%d\n", ip_vs_proto_name(iph->protocol), size); /* do TCP/UDP checksum checking if it has application helper */ if (cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { switch (skb->ip_summed) { case CHECKSUM_NONE: skb->csum = csum_partial(h.raw, size, 0); case CHECKSUM_HW: if (csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum)) { ip_vs_conn_put(cp); IP_VS_DBG_RL("Outgoing failed %s checksum " "from %d.%d.%d.%d (size=%d)!\n", ip_vs_proto_name(iph->protocol), NIPQUAD(iph->saddr), size); return NF_DROP; } break; default: /* CHECKSUM_UNNECESSARY */ break; } } IP_VS_DBG(11, "Outgoing %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])); /* mangle the packet */ iph->saddr = cp->vaddr; h.portp[0] = cp->vport; /* * Call application helper if needed */ if (ip_vs_app_pkt_out(cp, skb) != 0) { /* skb data has probably changed, update pointers */ iph = skb->nh.iph; h.raw = (char*)iph + ihl; size = skb->len - ihl; } /* * Adjust TCP/UDP checksums */ if (!cp->app && (iph->protocol != IPPROTO_UDP || h.uh->check != 0)) { /* Only port and addr are changed, do fast csum update */ ip_vs_fast_check_update(&h, cp->daddr, cp->vaddr, cp->dport, cp->vport, iph->protocol); if (skb->ip_summed == CHECKSUM_HW) skb->ip_summed = CHECKSUM_NONE; } else { /* full checksum calculation */ switch (iph->protocol) { case IPPROTO_TCP: h.th->check = 0; skb->csum = csum_partial(h.raw, size, 0); h.th->check = csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum); IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n", ip_vs_proto_name(iph->protocol), h.th->check, (char*)&(h.th->check) - (char*)h.raw); break; case IPPROTO_UDP: h.uh->check = 0; skb->csum = csum_partial(h.raw, size, 0); h.uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, size, iph->protocol, skb->csum); if (h.uh->check == 0) h.uh->check = 0xFFFF; IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n", ip_vs_proto_name(iph->protocol), h.uh->check, (char*)&(h.uh->check) - (char*)h.raw); break; } } ip_send_check(iph); ip_vs_out_stats(cp, skb); ip_vs_set_state(cp, VS_STATE_OUTPUT, iph, h.portp); ip_vs_conn_put(cp); skb->nfcache |= NFC_IPVS_PROPERTY; LeaveFunction(11); return NF_ACCEPT;}/* * Check if the packet is for VS/NAT connections, then send it * immediately. * Called by ip_fw_compact to detect packets for VS/NAT before * they are changed by ipchains masquerading code. */unsigned int check_for_ip_vs_out(struct sk_buff **skb_p, int (*okfn)(struct sk_buff *)){ unsigned int ret; ret = ip_vs_out(NF_IP_FORWARD, skb_p, NULL, NULL, NULL); if (ret != NF_ACCEPT) { return ret; } else { /* send the packet immediately if it is already mangled by ip_vs_out */ if ((*skb_p)->nfcache & NFC_IPVS_PROPERTY) { (*okfn)(*skb_p); return NF_STOLEN; } } return NF_ACCEPT;}/* * Handle ICMP messages in the outside-to-inside direction (incoming) * and sometimes in outgoing direction from ip_vs_forward_icmp. * Find any that might be relevant, check against existing connections, * forward to the right destination host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded. */static int ip_vs_in_icmp(struct sk_buff **skb_p){ struct sk_buff *skb = *skb_p; struct iphdr *iph; struct icmphdr *icmph; struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -