]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/libipsec/ip_packet.c
libipsec: Fix ip_packet_create_from_data() version field in IPv6 header
[thirdparty/strongswan.git] / src / libipsec / ip_packet.c
index 78b4c407a4291a36d856b727b92ff8e6edc1d826..d85bc96bef6e7cf95ec112a4e91e628a5aa6654a 100644 (file)
@@ -52,7 +52,15 @@ struct ip6_hdr {
        uint8_t ip6_hlim;
        struct in6_addr ip6_src, ip6_dst;
 } __attribute__((packed));
-#define HAVE_NETINET_IP6_H /* not really, but we only need the struct above */
+struct ip6_ext {
+       uint8_t ip6e_nxt;
+       uint8_t ip6e_len;
+} __attribute__((packed));
+#define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */
+#endif
+
+#ifndef IP_OFFMASK
+#define IP_OFFMASK 0x1fff
 #endif
 
 /**
@@ -219,6 +227,56 @@ static bool parse_transport_header(chunk_t packet, uint8_t proto,
        return TRUE;
 }
 
+#ifdef HAVE_NETINET_IP6_H
+/**
+ * Skip to the actual payload and parse the transport header.
+ */
+static bool parse_transport_header_v6(struct ip6_hdr *ip, chunk_t packet,
+                                                                         chunk_t *payload, uint8_t *proto,
+                                                                         uint16_t *sport, uint16_t *dport)
+{
+       struct ip6_ext *ext;
+       bool fragment = FALSE;
+
+       *proto = ip->ip6_nxt;
+       *payload = chunk_skip(packet, 40);
+       while (payload->len >= sizeof(struct ip6_ext))
+       {
+               switch (*proto)
+               {
+                       case 44:  /* Fragment Header */
+                               fragment = TRUE;
+                               /* skip the header */
+                       case 0:   /* Hop-by-Hop Options Header */
+                       case 43:  /* Routing Header */
+                       case 60:  /* Destination Options Header */
+                       case 135: /* Mobility Header */
+                       case 139: /* HIP */
+                       case 140: /* Shim6 */
+                               /* simply skip over these headers for now */
+                               ext = (struct ip6_ext*)payload->ptr;
+                               *proto = ext->ip6e_nxt;
+                               *payload = chunk_skip(*payload, 8 * (ext->ip6e_len + 1));
+                               continue;
+                       default:
+                               /* assume anything else is an upper layer protocol but only
+                                * attempt to parse the transport header for non-fragmented
+                                * packets as there is no guarantee that initial fragments
+                                * contain the transport header, depending on the number and
+                                * type of extension headers */
+                               if (!fragment &&
+                                       !parse_transport_header(*payload, *proto, sport, dport))
+                               {
+                                       return FALSE;
+                               }
+                               break;
+               }
+               break;
+       }
+       return TRUE;
+}
+#endif /* HAVE_NETINET_IP6_H */
+
 /**
  * Described in header.
  */
@@ -253,7 +311,8 @@ ip_packet_t *ip_packet_create(chunk_t packet)
                        /* remove any RFC 4303 TFC extra padding */
                        packet.len = min(packet.len, untoh16(&ip->ip_len));
                        payload = chunk_skip(packet, ip->ip_hl * 4);
-                       if (!parse_transport_header(payload, ip->ip_p, &sport, &dport))
+                       if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
+                               !parse_transport_header(payload, ip->ip_p, &sport, &dport))
                        {
                                goto failed;
                        }
@@ -277,10 +336,8 @@ ip_packet_t *ip_packet_create(chunk_t packet)
                        ip = (struct ip6_hdr*)packet.ptr;
                        /* remove any RFC 4303 TFC extra padding */
                        packet.len = min(packet.len, 40 + untoh16(&ip->ip6_plen));
-                       /* we only handle packets without extension headers, just skip the
-                        * basic IPv6 header */
-                       payload = chunk_skip(packet, 40);
-                       if (!parse_transport_header(payload, ip->ip6_nxt, &sport, &dport))
+                       if (!parse_transport_header_v6(ip, packet, &payload, &next_header,
+                                                                                  &sport, &dport))
                        {
                                goto failed;
                        }
@@ -288,7 +345,6 @@ ip_packet_t *ip_packet_create(chunk_t packet)
                                                                                 chunk_from_thing(ip->ip6_src), sport);
                        dst = host_create_from_chunk(AF_INET6,
                                                                                 chunk_from_thing(ip->ip6_dst), dport);
-                       next_header = ip->ip6_nxt;
                        break;
                }
 #endif /* HAVE_NETINET_IP6_H */
@@ -471,7 +527,7 @@ ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
                case AF_INET6:
                {
                        struct ip6_hdr ip = {
-                               .ip6_flow = htonl(6),
+                               .ip6_flow = htonl(6 << 28),
                                .ip6_plen = htons(data.len),
                                .ip6_nxt = next_header,
                                .ip6_hlim = 0x80,