📄 ipsec_tunnel.c
字号:
* This function assumes it is being called from dev_queue_xmit() * and that skb is filled properly by that function. */intipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev){ struct ipsecpriv *prv; /* Our device' private space */ struct sk_buff *oskb = NULL; /* Original skb pointer */ struct net_device_stats *stats; /* This device's statistics */ struct iphdr *iph; /* Our new IP header */ __u32 newdst; /* The other SG's IP address */ __u32 orgdst; /* Original IP destination address */ __u32 orgedst; /* 1st SG's IP address */ __u32 newsrc; /* The new source SG's IP address */ __u32 orgsrc; /* Original IP source address */ __u32 innersrc; /* Innermost IP source address */ int iphlen; /* IP header length */ int pyldsz; /* upper protocol payload size */ int headroom; int tailroom; int max_headroom = 0; /* The extra header space needed */ int max_tailroom = 0; /* The extra stuffing needed */ int ll_headroom; /* The extra link layer hard_header space needed */ int tot_headroom = 0; /* The total header space needed */ int tot_tailroom = 0; /* The totalstuffing needed */ __u8 *saved_header = NULL; /* saved copy of the hard header */ int i; struct sockaddr_encap matcher; /* eroute search key */ struct eroute *er; struct tdb *tdbp, *tdbq; /* Tunnel Descriptor Block pointers */ char sa[SATOA_BUF]; size_t sa_len; int hard_header_stripped = 0; /* has the hard header been removed yet? */ int hard_header_len = 0; struct device *physdev;/* struct device *virtdev; */ short physmtu; short mtudiff;#ifdef NET_21 struct rtable *rt = NULL;#endif /* NET_21 */ struct sa_id outgoing_said;#ifdef NET_21 int pass = 0;#endif /* NET_21 */ int error = 0; uint32_t eroute_pid = 0; /* * Return if there is nothing to do. (Does this ever happen?) XXX */ if (skb == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "Nothing to do!\n" ); goto cleanup; } if (dev == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "No device associated with skb!\n" ); goto cleanup; } prv = dev->priv; if (prv == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "Device has no private structure!\n" ); goto cleanup; } physdev = prv->dev; if (physdev == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "Device is not attached to physical device!\n" ); goto cleanup; } physmtu = physdev->mtu; stats = (struct net_device_stats *) &(prv->mystats);#ifdef NET_21 /* if skb was cloned (most likely due to a packet sniffer such as tcpdump being momentarily attached to the interface), make a copy of our own to modify */ if(skb_cloned(skb)) { if#ifdef SKB_COW_NEW (skb_cow(skb, skb_headroom(skb)) != 0)#else /* SKB_COW_NEW */ ((skb = skb_cow(skb, skb_headroom(skb))) == NULL)#endif /* SKB_COW_NEW */ { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "skb_cow failed to allocate buffer, dropping.\n" ); stats->tx_dropped++; goto cleanup; } }#endif /* NET_21 */#ifdef NET_21 iph = skb->nh.iph;#else /* NET_21 */ iph = skb->ip_hdr;#endif /* NET_21 */ /* sanity check for IP version as we can't handle IPv6 right now */ if (iph->version != 4) { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "found IP Version %d but cannot process other IP versions than v4.\n", iph->version); /* XXX */ stats->tx_dropped++; goto cleanup; } /* physdev->hard_header_len is unreliable and should not be used */ hard_header_len = (unsigned char *)iph - skb->data; if(hard_header_len < 0) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_tunnel_start_xmit: " "Negative hard_header_len (%d)?!\n", hard_header_len); stats->tx_dropped++; goto cleanup; } if(hard_header_len == 0) { /* no hard header present */ hard_header_stripped = 1; }#ifdef CONFIG_IPSEC_DEBUG if (debug_tunnel & DB_TN_XMIT) { int i; char c; printk(KERN_INFO "klips_debug:ipsec_tunnel_start_xmit: " ">>> skb->len=%ld hard_header_len:%d", (unsigned long int)skb->len, hard_header_len); c = ' '; for (i=0; i < hard_header_len; i++) { printk("%c%02x", c, skb->data[i]); c = ':'; } printk(" \n"); }#endif /* CONFIG_IPSEC_DEBUG */ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, iph); /* * Sanity checks */ if ((iph->ihl << 2) != sizeof (struct iphdr)) { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */ stats->tx_dropped++; goto cleanup; } #ifndef NET_21 /* TTL decrement code (on the way out!) borrowed from ip_forward.c */ if(0) { unsigned long checksum = iph->check; iph->ttl--; /* * Re-compute the IP header checksum. * This is efficient. We know what has happened to the header * and can thus adjust the checksum as Phil Karn does in KA9Q * except we do this in "network byte order". */ checksum += htons(0x0100); /* carry overflow? */ checksum += checksum >> 16; iph->check = checksum; } if (iph->ttl <= 0) { /* Tell the sender its packet died... */ ICMP_SEND(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, physdev); KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "TTL=0, too many hops!\n"); stats->tx_dropped++; goto cleanup; }#endif /* !NET_21 */ /* * First things first -- look us up in the erouting tables. */ matcher.sen_len = sizeof (struct sockaddr_encap); matcher.sen_family = AF_ENCAP; matcher.sen_type = SENT_IP4; matcher.sen_ip_src.s_addr = iph->saddr; matcher.sen_ip_dst.s_addr = iph->daddr; /* * The spinlock is to prevent any other process from accessing or deleting * the eroute while we are using and updating it. */ spin_lock(&eroute_lock); er = ipsec_findroute(&matcher); if(er) { outgoing_said = er->er_said; eroute_pid = er->er_pid; er->er_count++; er->er_lasttime = jiffies/HZ; } spin_unlock(&eroute_lock); /* * Quick cheat for now...are we udp/500? If so, let it through * without interference since it is most likely an IKE packet. */ if((ip_chk_addr((unsigned long)iph->saddr) == IS_MYADDR) && ((!er) || (iph->daddr == outgoing_said.dst.s_addr) || (INADDR_ANY == outgoing_said.dst.s_addr)) && (iph->protocol == IPPROTO_UDP) && (ntohs(((struct udphdr*)((caddr_t)iph + (iph->ihl << 2)))->dest) == 500)) {#ifdef NET_21 pass = 1;#endif /* NET_21 */ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "udp/500 IKE packet, sending unprocessed, " "calling dev_queue_xmit\n"); goto bypass; } KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_tunnel_start_xmit: " "Original head,tailroom: %d,%d\n", skb_headroom(skb), skb_tailroom(skb)); innersrc = iph->saddr; /* start encapsulation loop here XXX */ do { struct tdb *tdbprev = NULL; newdst = orgdst = iph->daddr; newsrc = orgsrc = iph->saddr; orgedst = outgoing_said.dst.s_addr; iphlen = iph->ihl << 2; pyldsz = ntohs(iph->tot_len) - iphlen; max_headroom = max_tailroom = 0; if(er == NULL || (outgoing_said.proto==IPPROTO_INT && outgoing_said.spi==htonl(SPI_DROP))) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "shunt SA of DROP or no eroute: dropping.\n"); stats->tx_dropped++; goto cleanup; } if(outgoing_said.proto==IPPROTO_INT && outgoing_said.spi==htonl(SPI_REJECT)) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "shunt SA of REJECT: notifying when coded... and dropping.\n"); stats->tx_dropped++; goto cleanup; } if(outgoing_said.proto==IPPROTO_INT && outgoing_said.spi==htonl(SPI_PASS)) {#ifdef NET_21 pass = 1;#endif /* NET_21 */ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "shunt SA of PASS: calling dev_queue_xmit\n"); goto bypass; } if(outgoing_said.proto==IPPROTO_INT && outgoing_said.spi==htonl(SPI_HOLD)) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "shunt SA of HOLD: will stash when coded..., dropping for now.\n"); stats->tx_dropped++; goto cleanup; } if(outgoing_said.proto==IPPROTO_INT && outgoing_said.spi==htonl(SPI_TRAP)) { struct tdb tdb; struct sockaddr_in src, dst;#ifdef CONFIG_IPSEC_DEBUG char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF];#endif /* CONFIG_IPSEC_DEBUG */ struct eroute hold_eroute; struct sa_id hold_said; /* Signal all listening KMds with a PF_KEY ACQUIRE */ tdb.tdb_said.proto = iph->protocol; src.sin_family = AF_INET; dst.sin_family = AF_INET; src.sin_addr.s_addr = iph->saddr; dst.sin_addr.s_addr = iph->daddr; src.sin_port = (iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)iph) + (iph->ihl << 2)))->source : (iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)iph + (iph->ihl << 2)))->source : 0)); dst.sin_port = (iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)iph) + (iph->ihl << 2)))->dest : (iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)iph + (iph->ihl << 2)))->dest : 0)); for(i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) { src.sin_zero[i] = 0; dst.sin_zero[i] = 0; } tdb.tdb_addr_s = (struct sockaddr*)(&src); tdb.tdb_addr_d = (struct sockaddr*)(&dst); KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_tunnel_start_xmit: " "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n", addrtoa(((struct sockaddr_in*)(tdb.tdb_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR", ((struct sockaddr_in*)(tdb.tdb_addr_s))->sin_port, addrtoa(((struct sockaddr_in*)(tdb.tdb_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR", ((struct sockaddr_in*)(tdb.tdb_addr_d))->sin_port, tdb.tdb_said.proto); pfkey_acquire(&tdb); /* install HOLD eroute */ memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute)); memset((caddr_t)&hold_said, 0, sizeof(hold_said)); hold_said.proto = IPPROTO_INT; hold_said.spi = htonl(SPI_HOLD); hold_said.dst.s_addr = 0L; hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap); hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap); hold_eroute.er_eaddr.sen_family = AF_ENCAP; hold_eroute.er_emask.sen_family = AF_ENCAP; hold_eroute.er_eaddr.sen_type = SENT_IP4; hold_eroute.er_emask.sen_type = 255; hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr; hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr; hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST; hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST; hold_eroute.er_pid = eroute_pid; hold_eroute.er_count = 0; hold_eroute.er_lasttime = jiffies/HZ;#ifdef IPSEC_CONFIG_FULL_SELECTOR_LIST /* These are ficticious. Don't uncomment these until proto, sport and dport exist in the SPDB */ hold_eroute.er_proto = iph->protocol; hold_eroute.er_src_port = (iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)iph) + (iph->ihl << 2)))->source : (iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)iph + (iph->ihl << 2)))->source : 0)); hold_eroute.er_dst_port = (iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)iph) + (iph->ihl << 2)))->dest : (iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)iph + (iph->ihl << 2)))->dest : 0));#endif /* IPSEC_CONFIG_FULL_SELECTOR_LIST */#ifdef CONFIG_IPSEC_DEBUG if (debug_pfkey) { char buf1[64], buf2[64]; subnettoa(hold_eroute.er_eaddr.sen_ip_src, hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); subnettoa(hold_eroute.er_eaddr.sen_ip_dst, hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "calling breakeroute and makeroute for %s->%s HOLD eroute.\n", buf1, buf2); }#endif /* CONFIG_IPSEC_DEBUG */ if (!(ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask)) == EINVAL)) { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "breakeroute should have failed.\n"); /* SENDERR(-error); */ } else { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD breakeroute found nothing as expected.\n"); } if ((error = ipsec_makeroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask), hold_said, eroute_pid))) { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD makeroute returned %d, failed.\n", error); /* SENDERR(-error); */ } else { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD makeroute call successful.\n"); } goto cleanup; } /* The spinlock is to prevent any other process from accessing or deleting the TDB hash table or any of the TDBs while we are using and updating them.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -