]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet/udp_usrreq.c
xnu-517.9.4.tar.gz
[apple/xnu.git] / bsd / netinet / udp_usrreq.c
index 4dde880cef51ad2fb5cddd37526c2f0ecf9c51a1..5d7ce515160bee398a13b0a8ed04915bcb4d3282 100644 (file)
@@ -101,8 +101,6 @@ extern int ipsec_bypass;
 #define DBG_FNC_UDP_INPUT      NETDBG_CODE(DBG_NETUDP, (5 << 8))
 #define DBG_FNC_UDP_OUTPUT     NETDBG_CODE(DBG_NETUDP, (6 << 8) | 1)
 
-
-#define __STDC__ 1
 /*
  * UDP protocol implementation.
  * Per RFC 768, August, 1980.
@@ -132,6 +130,8 @@ struct      inpcbinfo udbinfo;
 #endif
 
 extern  int apple_hwcksum_rx;
+extern int     esp_udp_encap_port;
+extern u_long  route_generation;
 
 struct udpstat udpstat;        /* from udp_var.h */
 SYSCTL_STRUCT(_net_inet_udp, UDPCTL_STATS, stats, CTLFLAG_RD,
@@ -289,17 +289,26 @@ udp_input(m, iphlen)
         * Checksum extended UDP header and data.
         */
        if (uh->uh_sum) {
-               if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
-                        if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
-                                uh->uh_sum = m->m_pkthdr.csum_data;
-                        else
-                           goto doudpcksum;
-                      uh->uh_sum ^= 0xffff;
-                } else {
+               if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
+                       if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
+                               uh->uh_sum = m->m_pkthdr.csum_data;
+                       else
+                               goto doudpcksum;
+                       uh->uh_sum ^= 0xffff;
+               } else {
+                       char b[9];
 doudpcksum:
+                       *(uint32_t*)&b[0] = *(uint32_t*)&((struct ipovly *)ip)->ih_x1[0];
+                       *(uint32_t*)&b[4] = *(uint32_t*)&((struct ipovly *)ip)->ih_x1[4];
+                       *(uint8_t*)&b[8] = *(uint8_t*)&((struct ipovly *)ip)->ih_x1[8];
+                       
                        bzero(((struct ipovly *)ip)->ih_x1, 9);
                        ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
                        uh->uh_sum = in_cksum(m, len + sizeof (struct ip));
+                       
+                       *(uint32_t*)&((struct ipovly *)ip)->ih_x1[0] = *(uint32_t*)&b[0];
+                       *(uint32_t*)&((struct ipovly *)ip)->ih_x1[4] = *(uint32_t*)&b[4];
+                       *(uint8_t*)&((struct ipovly *)ip)->ih_x1[8] = *(uint8_t*)&b[8];
                }
                if (uh->uh_sum) {
                        udpstat.udps_badsum++;
@@ -417,6 +426,53 @@ doudpcksum:
                udp_append(last, ip, m, iphlen + sizeof(struct udphdr));
                return;
        }
+
+       /*
+        * UDP to port 4500 with a payload where the first four bytes are
+        * not zero is a UDP encapsulated IPSec packet. Packets where
+        * the payload is one byte and that byte is 0xFF are NAT keepalive
+        * packets. Decapsulate the ESP packet and carry on with IPSec input
+        * or discard the NAT keep-alive.
+        */
+       if (ipsec_bypass == 0 && (esp_udp_encap_port & 0xFFFF) != 0 &&
+               uh->uh_dport == ntohs((u_short)esp_udp_encap_port)) {
+               int     payload_len = len - sizeof(struct udphdr) > 4 ? 4 : len - sizeof(struct udphdr);
+               if (m->m_len < iphlen + sizeof(struct udphdr) + payload_len) {
+                       if ((m = m_pullup(m, iphlen + sizeof(struct udphdr) + payload_len)) == 0) {
+                               udpstat.udps_hdrops++;
+                               KERNEL_DEBUG(DBG_FNC_UDP_INPUT | DBG_FUNC_END, 0,0,0,0,0);
+                               return;
+                       }
+                       ip = mtod(m, struct ip *);
+                       uh = (struct udphdr *)((caddr_t)ip + iphlen);
+               }
+               /* Check for NAT keepalive packet */
+               if (payload_len == 1 && *(u_int8_t*)((caddr_t)uh + sizeof(struct udphdr)) == 0xFF) {
+                       m_freem(m);
+                       KERNEL_DEBUG(DBG_FNC_UDP_INPUT | DBG_FUNC_END, 0,0,0,0,0);
+                       return;
+               }
+               else if (payload_len == 4 && *(u_int32_t*)((caddr_t)uh + sizeof(struct udphdr)) != 0) {
+                       /* UDP encapsulated IPSec packet to pass through NAT */
+                       size_t stripsiz;
+
+                       stripsiz = sizeof(struct udphdr);
+
+                       ip = mtod(m, struct ip *);
+                       ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), iphlen);
+                       m->m_data += stripsiz;
+                       m->m_len -= stripsiz;
+                       m->m_pkthdr.len -= stripsiz;
+                       ip = mtod(m, struct ip *);
+                       ip->ip_len = ip->ip_len - stripsiz;
+                       ip->ip_p = IPPROTO_ESP;
+
+                       KERNEL_DEBUG(DBG_FNC_UDP_INPUT | DBG_FUNC_END, 0,0,0,0,0);
+                       esp4_input(m, iphlen);
+                       return;
+               }
+       }
+
        /*
         * Locate pcb for datagram.
         */
@@ -745,6 +801,24 @@ udp_output(inp, m, addr, control, p)
                goto release;
        }
 
+       /* If there was a routing change, discard cached route and check
+        * that we have a valid source address. 
+        * Reacquire a new source address if INADDR_ANY was specified
+        */
+
+       if (inp->inp_route.ro_rt && inp->inp_route.ro_rt->generation_id != route_generation) {
+               if (ifa_foraddr(inp->inp_laddr.s_addr) == NULL) { /* src address is gone */
+                       if (inp->inp_flags & INP_INADDR_ANY)
+                               inp->inp_faddr.s_addr = INADDR_ANY; /* new src will be set later */
+                       else {
+                               error = EADDRNOTAVAIL;
+                               goto release;
+                       }
+               }
+               rtfree(inp->inp_route.ro_rt);
+               inp->inp_route.ro_rt = (struct rtentry *)0;
+       }
+
        if (addr) {
                laddr = inp->inp_laddr;
                if (inp->inp_faddr.s_addr != INADDR_ANY) {
@@ -766,6 +840,8 @@ udp_output(inp, m, addr, control, p)
                        goto release;
                }
        }
+
+
        /*
         * Calculate data length and get a mbuf
         * for UDP and IP headers.
@@ -773,9 +849,7 @@ udp_output(inp, m, addr, control, p)
        M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
        if (m == 0) {
                error = ENOBUFS;
-               if (addr)
-                       splx(s);
-               goto release;
+               goto abort;
        }
 
        /*
@@ -813,7 +887,7 @@ udp_output(inp, m, addr, control, p)
 #if IPSEC
        if (ipsec_bypass == 0 && ipsec_setsocket(m, inp->inp_socket) != 0) {
                error = ENOBUFS;
-               goto release;
+               goto abort;
        }
 #endif /*IPSEC*/
        error = ip_output(m, inp->inp_options, &inp->inp_route,
@@ -828,6 +902,13 @@ udp_output(inp, m, addr, control, p)
        KERNEL_DEBUG(DBG_FNC_UDP_OUTPUT | DBG_FUNC_END, error, 0,0,0,0);
        return (error);
 
+abort:
+        if (addr) {
+                in_pcbdisconnect(inp);
+                inp->inp_laddr = laddr; /* XXX rehash? */
+                splx(s);
+        }
+
 release:
        m_freem(m);
        KERNEL_DEBUG(DBG_FNC_UDP_OUTPUT | DBG_FUNC_END, error, 0,0,0,0);