From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by passt.top (Postfix) with ESMTPS id 93EF55A004E for ; Sat, 10 Aug 2024 18:00:36 +0200 (CEST) Received: by mail-wr1-x42e.google.com with SMTP id ffacd0b85a97d-36bd70f6522so1646389f8f.1 for ; Sat, 10 Aug 2024 09:00:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1723305636; x=1723910436; darn=passt.top; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=24twlHVKSk4uLzJJFA9Y8MmRZyKoJZkwJa9ZSdMEQeM=; b=YH/ZAtiw99egv9Mc24LbwIQnMwiphMVHPA4W+aEGHH7PwDKMx5kzK9J/3R7aazxLtH wlxB0Lu5wfpPAnr483eVeBkbjY7i1WuFAifK0A6LXCEBkGRlueKk92Hf8MQ+oZuu5sGN WgHusTQUObm8tXMvObWRSsmtI/OrIvjrkkQhs2KxrA19mgbhfHWi68bEajAaj7s2m0Op gagNf/gtQ8imVVF0mRBK8/eYD6xGkPiZVW8+FQrE939hKOCtJZz+v+K1N5tM5ICqcV8K JbhBJq4Ii2cKr5yU+qIqfRDbViIF49cXACPF7YYgJY3PG1Sr+wFyt08j5S8vdBHHAFOP /Tqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723305636; x=1723910436; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=24twlHVKSk4uLzJJFA9Y8MmRZyKoJZkwJa9ZSdMEQeM=; b=NfdbOIS1JKXlAfCoSHLBmfQXULjYgcXKHN1v3Oiv89w6Vf5osivoqzN2NOegQW74V5 YMKB80qQ0cPM1lwVMxYem1d/usX7I1oLPlAZrmpmNtpUJzTGtJc5KCKjXPIphr3HcLMg izDLM+pIBQP+tl1ZkNsEkSnM+TbsjTugAKTdpR6J3aG7nkFZdePlEdFKcuKMAneUhukv tb1BZm4cPCQ7wwKahBK+3NvY4spkIgrwDoF3bvJioAbcnMItmRljqIfF9+pGWhSbCuem Mae8P9zk/ynLKwNTJ/Z/w/uvA0OswqV53SQE/fE2eD8B79Oustl3lO8/pixA3VM79Tow R3vw== X-Gm-Message-State: AOJu0YxOQX3StuTIb91Th8JLOpAFo8EZ+8L9Kv98hUEt28AE4zmqSnIf kQiNbcJV9KRiEHT760dmLnfS8pL5T6Tsf2NXfCv5qXnhZdwCI2qmayiCDiH9O6s= X-Google-Smtp-Source: AGHT+IGJUWVKwGQR1SvU8VX/gcUggEWc8VZyJn8/HbooeP56ifbIFIGDf2vrYQWmIJwHX7Y/LEQaFA== X-Received: by 2002:a5d:6341:0:b0:368:57dd:3822 with SMTP id ffacd0b85a97d-36d5eeddcffmr3685841f8f.37.1723305634993; Sat, 10 Aug 2024 09:00:34 -0700 (PDT) Received: from 192.168.1.5 ([197.40.156.185]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36e4e51ea82sm2746436f8f.72.2024.08.10.09.00.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 10 Aug 2024 09:00:34 -0700 (PDT) From: AbdAlRahman Gad To: passt-dev@passt.top Subject: [PATCH] ndp.c: Turn NDP responder into more declarative implementation Date: Sat, 10 Aug 2024 19:00:12 +0300 Message-ID: <20240810160012.137838-1-abdobngad@gmail.com> X-Mailer: git-send-email 2.46.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: TPNXKEGEVNXEZ2ZV55ZKHGDNX3G5OKVO X-Message-ID-Hash: TPNXKEGEVNXEZ2ZV55ZKHGDNX3G5OKVO X-MailFrom: abdobngad@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: AbdAlRahman Gad X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: - Add structs for NA, RA, NS, MTU, prefix info, option header, link-layer address, RDNSS, DNSSL and link-layer for RA message. - Turn NA message from purely imperative, going byte by byte, to declarative by filling it's struct. - Turn part of RA message into declarative. - Move packet_add() to be before the call of ndp() in tap6_handler() if the protocol of the packet is ICMPv6. - Add a pool of packets as an additional parameter to ndp(). - Check the size of NS packet with packet_get() before sending an NA packet. - Add documentation for the structs. - Add an enum for NDP option types. Link: https://bugs.passt.top/show_bug.cgi?id=21 Signed-off-by: AbdAlRahman Gad --- ndp.c | 320 +++++++++++++++++++++++++++++++++++++++++++--------------- ndp.h | 3 +- tap.c | 5 +- 3 files changed, 246 insertions(+), 82 deletions(-) diff --git a/ndp.c b/ndp.c index cea3df5..5e9cb78 100644 --- a/ndp.c +++ b/ndp.c @@ -38,22 +38,199 @@ #define NS 135 #define NA 136 +enum ndp_option_types { + OPT_SRC_L2_ADDR = 1, + OPT_TARGET_L2_ADDR = 2, + OPT_PREFIX_INFO = 3, + OPT_MTU = 5, + OPT_RDNSS_TYPE = 25, + OPT_DNSSL_TYPE = 31, +}; + +/** + * struct opt_header - Option header + * @type: Option type + * @len: Option length, in units of 8 bytes +*/ +struct opt_header { + uint8_t type; + uint8_t len; +} __attribute__((packed)); + +/** + * struct opt_l2_addr - Link-layer address + * @header: Option header + * @mac: MAC address + */ +struct opt_l2_addr { + struct opt_header header; + unsigned char mac[ETH_ALEN]; +} __attribute__((packed)); + +/** + * struct ndp_na - NDP Neighbor Advertisement (NA) message + * @ih: ICMPv6 header + * @target_addr: Target IPv6 address + * @target_l2_addr: Target link-layer address + */ +struct ndp_na { + struct icmp6hdr ih; + struct in6_addr target_addr; + struct opt_l2_addr target_l2_addr; +} __attribute__((packed)); + +/** + * struct opt_prefix_info - Prefix Information option + * @header: Option header + * @prefix_len: The number of leading bits in the Prefix that are valid + * @prefix_flags: Flags associated with the prefix + * @valid_lifetime: Valid lifetime (ms) + * @pref_lifetime: Preferred lifetime (ms) + * @reserved: Unused + */ +struct opt_prefix_info { + struct opt_header header; + uint8_t prefix_len; + uint8_t prefix_flags; + uint32_t valid_lifetime; + uint32_t pref_lifetime; + uint32_t reserved; +} __attribute__((packed)); + +/** + * struct mtu_opt - Maximum transmission unit (MTU) option + * @header: Option header + * @reserved: Unused + * @value: MTU value, network order + */ +struct mtu_opt { + struct opt_header header; + uint16_t reserved; + uint32_t value; +} __attribute__((packed)); + +/** + * struct rdnss - Recursive DNS Server (RDNSS) option + * @header: Option header + * @reserved: Unused + * @lifetime: Validity time (s) + * @dns: List of DNS server addresses + */ +struct opt_rdnss { + struct opt_header header; + uint16_t reserved; + uint32_t lifetime; + struct in6_addr dns[MAXNS + 1]; +} __attribute__((packed)); + +/** + * struct dnssl - DNS Search List (DNSSL) option + * @header: Option header + * @reserved: Unused + * @lifetime: Validity time (s) + * @domains: List of NULL-seperated search domains + */ +struct opt_dnssl { + struct opt_header header; + uint16_t reserved; + uint32_t lifetime; + unsigned char domains[MAXDNSRCH * NS_MAXDNAME]; +} __attribute__((packed)); + +/** + * struct ndp_ra - NDP Router Advertisement (RA) message + * @ih: ICMPv6 header + * @reachable: Reachability time, after confirmation (ms) + * @retrans: Time between retransmitted NS messages (ms) + * @prefix_info: Prefix Information option + * @prefix: IPv6 prefix + * @mtu: MTU option + * @source_ll: Target link-layer address + * @dns: RDNSS and DNSSL options + */ +struct ndp_ra { + struct icmp6hdr ih; + uint32_t reachable; + uint32_t retrans; + struct opt_prefix_info prefix_info; + struct in6_addr prefix; + struct mtu_opt mtu; + struct opt_l2_addr source_ll; + + unsigned char dns[sizeof(struct opt_rdnss) + sizeof(struct opt_dnssl)]; +} __attribute__((packed)); + +/** + * struct ndp_ns - NDP Neighbor Solicitation (NS) message + * @ih: ICMPv6 header + * @target_addr: Target IPv6 address + */ +struct ndp_ns { + struct icmp6hdr ih; + struct in6_addr target_addr; +} __attribute__((packed)); + /** * ndp() - Check for NDP solicitations, reply as needed * @c: Execution context * @ih: ICMPv6 header - * @saddr Source IPv6 address + * @saddr: Source IPv6 address + * @p: Packet pool * * Return: 0 if not handled here, 1 if handled, -1 on failure */ -int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr) +int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr, + const struct pool *p) { + struct ndp_na na = { + .ih = { + .icmp6_type = NA, + .icmp6_code = 0, + .icmp6_router = 1, + .icmp6_solicited = 1, + .icmp6_override = 1, + }, + .target_l2_addr = { + .header = { + .type = OPT_TARGET_L2_ADDR, + .len = 1, + }, + } + }; + struct ndp_ra ra = { + .ih = { + .icmp6_type = RA, + .icmp6_code = 0, + .icmp6_hop_limit = 255, + /* RFC 8319 */ + .icmp6_rt_lifetime = htons_constant(65535), + .icmp6_addrconf_managed = 1 + }, + .prefix_info = { + .header = { + .type = OPT_PREFIX_INFO, + .len = 4, + }, + .prefix_len = 64, + .prefix_flags = 0xc0, /* prefix flags: L, A */ + .valid_lifetime = ~0U, + .pref_lifetime = ~0U, + }, + .mtu = { + .header = { + .type = OPT_MTU, + .len = 1, + }, + }, + .source_ll = { + .header = { + .type = OPT_SRC_L2_ADDR, + .len = 1, + }, + }, + }; const struct in6_addr *rsaddr; /* src addr for reply */ - char buf[BUFSIZ] = { 0 }; - struct ipv6hdr *ip6hr; - struct icmp6hdr *ihr; - struct ethhdr *ehr; - unsigned char *p; + unsigned char *ptr = NULL; size_t dlen; if (ih->icmp6_type < RS || ih->icmp6_type > NA) @@ -62,28 +239,22 @@ int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr) if (c->no_ndp) return 1; - ehr = (struct ethhdr *)buf; - ip6hr = (struct ipv6hdr *)(ehr + 1); - ihr = (struct icmp6hdr *)(ip6hr + 1); - if (ih->icmp6_type == NS) { + struct ndp_ns *ns = packet_get(p, 0, 0, sizeof(struct ndp_ns), + NULL); + + if (!ns) + return -1; + if (IN6_IS_ADDR_UNSPECIFIED(saddr)) return 1; info("NDP: received NS, sending NA"); - ihr->icmp6_type = NA; - ihr->icmp6_code = 0; - ihr->icmp6_router = 1; - ihr->icmp6_solicited = 1; - ihr->icmp6_override = 1; - - p = (unsigned char *)(ihr + 1); - memcpy(p, ih + 1, sizeof(struct in6_addr)); /* target address */ - p += 16; - *p++ = 2; /* target ll */ - *p++ = 1; /* length */ - memcpy(p, c->mac, ETH_ALEN); - p += 6; + + memcpy(&na.target_addr, &ns->target_addr, + sizeof(na.target_addr)); + memcpy(na.target_l2_addr.mac, c->mac, ETH_ALEN); + } else if (ih->icmp6_type == RS) { size_t dns_s_len = 0; int i, n; @@ -92,91 +263,76 @@ int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr) return 1; info("NDP: received RS, sending RA"); - ihr->icmp6_type = RA; - ihr->icmp6_code = 0; - ihr->icmp6_hop_limit = 255; - ihr->icmp6_rt_lifetime = htons(65535); /* RFC 8319 */ - ihr->icmp6_addrconf_managed = 1; - - p = (unsigned char *)(ihr + 1); - p += 8; /* reachable, retrans time */ - *p++ = 3; /* prefix */ - *p++ = 4; /* length */ - *p++ = 64; /* prefix length */ - *p++ = 0xc0; /* prefix flags: L, A */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; - *(uint32_t *)p = (uint32_t)~0U; /* preferred lifetime */ - p += 8; - memcpy(p, &c->ip6.addr, 8); /* prefix */ - p += 16; - - if (c->mtu != -1) { - *p++ = 5; /* type */ - *p++ = 1; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = htonl(c->mtu); /* MTU */ - p += 4; - } + memcpy(&ra.prefix, &c->ip6.addr, sizeof(ra.prefix)); + + if (c->mtu != -1) + ra.mtu.value = htonl(c->mtu); + + ptr = &ra.dns[0]; if (c->no_dhcp_dns) goto dns_done; for (n = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n]); n++); if (n) { - *p++ = 25; /* RDNSS */ - *p++ = 1 + 2 * n; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; - + struct opt_rdnss *rdnss = (struct opt_rdnss *)ptr; + *rdnss = (struct opt_rdnss) { + .header = { + .type = OPT_RDNSS_TYPE, + .len = 1 + 2 * n, + }, + .lifetime = ~0U, + }; for (i = 0; i < n; i++) { - memcpy(p, &c->ip6.dns[i], 16); /* address */ - p += 16; + memcpy(&rdnss->dns[i], &c->ip6.dns[i], + sizeof(rdnss->dns[i])); } + ptr += offsetof(struct opt_rdnss, dns) + + i * sizeof(rdnss->dns[0]); for (n = 0; *c->dns_search[n].n; n++) dns_s_len += strlen(c->dns_search[n].n) + 2; } if (!c->no_dhcp_dns_search && dns_s_len) { - *p++ = 31; /* DNSSL */ - *p++ = (dns_s_len + 8 - 1) / 8 + 1; /* length */ - p += 2; /* reserved */ - *(uint32_t *)p = (uint32_t)~0U; /* lifetime */ - p += 4; + struct opt_dnssl *dnssl = (struct opt_dnssl *)ptr; + *dnssl = (struct opt_dnssl) { + .header = { + .type = OPT_DNSSL_TYPE, + .len = DIV_ROUND_UP(dns_s_len, 8) + 1, + }, + .lifetime = ~0U, + }; + ptr = dnssl->domains; for (i = 0; i < n; i++) { + size_t len; char *dot; - *(p++) = '.'; + *(ptr++) = '.'; - strncpy((char *)p, c->dns_search[i].n, - sizeof(buf) - - ((intptr_t)p - (intptr_t)buf)); - for (dot = (char *)p - 1; *dot; dot++) { + len = sizeof(dnssl->domains) - + (ptr - dnssl->domains); + + strncpy((char *)ptr, c->dns_search[i].n, len); + for (dot = (char *)ptr - 1; *dot; dot++) { if (*dot == '.') *dot = strcspn(dot + 1, "."); } - p += strlen(c->dns_search[i].n); - *(p++) = 0; + ptr += strlen(c->dns_search[i].n); + *(ptr++) = 0; } - memset(p, 0, 8 - dns_s_len % 8); /* padding */ - p += 8 - dns_s_len % 8; + memset(ptr, 0, 8 - dns_s_len % 8); /* padding */ + ptr += 8 - dns_s_len % 8; } dns_done: - *p++ = 1; /* source ll */ - *p++ = 1; /* length */ - memcpy(p, c->mac, ETH_ALEN); - p += 6; + memcpy(&ra.source_ll.mac, c->mac, ETH_ALEN); } else { return 1; } - dlen = (uintptr_t)p - (uintptr_t)ihr - sizeof(*ihr); - if (IN6_IS_ADDR_LINKLOCAL(saddr)) c->ip6.addr_ll_seen = *saddr; else @@ -187,7 +343,13 @@ dns_done: else rsaddr = &c->ip6.addr_ll; - tap_icmp6_send(c, rsaddr, saddr, ihr, dlen + sizeof(*ihr)); + if (ih->icmp6_type == NS) { + dlen = sizeof(struct ndp_na); + tap_icmp6_send(c, rsaddr, saddr, &na, dlen); + } else if (ih->icmp6_type == RS) { + dlen = ptr - (unsigned char *)&ra; + tap_icmp6_send(c, rsaddr, saddr, &ra, dlen); + } return 1; } diff --git a/ndp.h b/ndp.h index b33cd69..a786441 100644 --- a/ndp.h +++ b/ndp.h @@ -6,6 +6,7 @@ #ifndef NDP_H #define NDP_H -int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr); +int ndp(struct ctx *c, const struct icmp6hdr *ih, const struct in6_addr *saddr, + const struct pool *p); #endif /* NDP_H */ diff --git a/tap.c b/tap.c index 5852705..87be3a6 100644 --- a/tap.c +++ b/tap.c @@ -808,12 +808,13 @@ resume: if (l4len < sizeof(struct icmp6hdr)) continue; - if (ndp(c, (struct icmp6hdr *)l4h, saddr)) + packet_add(pkt, l4len, l4h); + + if (ndp(c, (struct icmp6hdr *)l4h, saddr, pkt)) continue; tap_packet_debug(NULL, ip6h, NULL, proto, NULL, 1); - packet_add(pkt, l4len, l4h); icmp_tap_handler(c, PIF_TAP, AF_INET6, saddr, daddr, pkt, now); continue; -- 2.46.0