
Add TCP/IP6 related headers and reuse refactored TCP/IP implementation
Signed-off-by: Dmitrii Merkurev dimorinny@google.com Cc: Ying-Chun Liu (PaulLiu) paul.liu@linaro.org Cc: Simon Glass sjg@chromium.org Сс: Joe Hershberger joe.hershberger@ni.com Сс: Ramon Fried rfried.dev@gmail.com Reviewed-by: Ying-Chun Liu (PaulLiu) paul.liu@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- include/net/tcp6.h | 103 ++++++++++++++++++++++++++++++++++ include/net6.h | 21 +++++++ net/Makefile | 1 + net/net.c | 7 ++- net/net6.c | 134 +++++++++++++++++++++++++++++++++++---------- net/tcp6.c | 97 ++++++++++++++++++++++++++++++++ 6 files changed, 332 insertions(+), 31 deletions(-) create mode 100644 include/net/tcp6.h create mode 100644 net/tcp6.c
diff --git a/include/net/tcp6.h b/include/net/tcp6.h new file mode 100644 index 0000000000..682480cdb4 --- /dev/null +++ b/include/net/tcp6.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2022 The Android Open Source Project + */ + +#ifndef __TCP6_H__ +#define __TCP6_H__ + +#include <net6.h> +#include <net/tcp.h> + +/** + * typedef rxhand_tcp6_f() - An incoming TCP IPv6 packet handler. + * @pkt: pointer to the application packet + * @dport: destination TCP port + * @sip: source IP6 address + * @sport: source TCP port + * @tcp_seq_num: TCP sequential number + * @tcp_ack_num: TCP acknowledgment number + * @action: TCP packet type (SYN, ACK, FIN, etc) + */ +typedef void rxhand_tcp6_f(uchar *pkt, u16 dport, + struct in6_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len); + +/** + * struct ip6_tcp_hdr_o - IP6 + TCP header + TCP options + * @ip_hdr: IP6 + TCP header + * @tcp_hdr: TCP header + * @tcp_o: TCP options + * @end: end of IP6/TCP header + */ +struct ip6_tcp_hdr_o { + struct ip6_hdr ip_hdr; + struct tcp_hdr tcp_hdr; + struct tcp_hdr_o tcp_o; + u8 end; +} __packed; + +#define IP6_TCP_O_SIZE (sizeof(struct ip6_tcp_hdr_o)) + +/** + * struct ip6_tcp_hdr_s - IP6 + TCP header + TCP options + * @ip_hdr: IP6 + TCP header + * @tcp_hdr: TCP header + * @t_opt: TCP Timestamp Option + * @sack_v: TCP SACK Option + * @end: end of options + */ +struct ip6_tcp_hdr_s { + struct ip6_hdr ip_hdr; + struct tcp_hdr tcp_hdr; + struct tcp_t_opt t_opt; + struct tcp_sack_v sack_v; + u8 end; +} __packed; + +#define IP6_TCP_SACK_SIZE (sizeof(struct ip6_tcp_hdr_s)) + +/** + * union tcp6_build_pkt - union for building TCP/IP6 packet. + * @ip: IP6 and TCP header plus TCP options + * @sack: IP6 and TCP header plus SACK options + * @raw: buffer + */ +union tcp6_build_pkt { + struct ip6_tcp_hdr_o ip; + struct ip6_tcp_hdr_s sack; + uchar raw[1600]; +} __packed; + +/** + * net_set_tcp6_handler6() - set application TCP6 packet handler + * @param f pointer to callback function + */ +void net_set_tcp_handler6(rxhand_tcp6_f *f); + +/** + * net_set_tcp_header6() - IPv6 TCP header bulding implementation + * @pkt_ip_hdr: pointer to IP6 header + * @dport: destination TCP port + * @sport: source TCP port + * @payload_len: payload length + * @action: TCP packet type (SYN, ACK, FIN, etc) + * @tcp_seq_num: TCP sequential number + * @tcp_ack_num: TCP acknowledgment number + * + * returns TCP header size + */ +int net_set_tcp_header6(uchar *pkt_ip_hdr, u16 dport, u16 sport, int payload_len, + u8 action, u32 tcp_seq_num, u32 tcp_ack_num); + +void net_set_tcp_handler6(rxhand_tcp6_f *f); + +/** + * rxhand_tcp6() - handle incoming IP6 TCP packet + * @param b pointer to IP6/TCP packet builder struct + * @param len full packet length + */ +void rxhand_tcp6(union tcp6_build_pkt *b, unsigned int len); + +#endif // __TCP6_H__ diff --git a/include/net6.h b/include/net6.h index beafc05338..80f9723a05 100644 --- a/include/net6.h +++ b/include/net6.h @@ -344,6 +344,20 @@ int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest, int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport, int len);
+/** + * net_send_tcp_packet6() - Make up TCP packet and send it + * + * @payload_len: TCP payload length + * @dport: destination port + * @sport: source port + * @action: TCP flag (SYN, ACL, PUSH, etc) + * @tcp_seq_num: TCP sequence number + * @tcp_ack_num: TCP ackno + * Return: 0 if send successfully, -1 otherwise + */ +int net_send_tcp_packet6(int payload_len, int dport, int sport, u8 action, + u32 tcp_seq_num, u32 tcp_ack_num); + /** * net_ip6_handler() - Handle IPv6 packet * @@ -432,6 +446,13 @@ net_send_udp_packet6(uchar *ether, struct in6_addr *dest, return -1; }
+static inline int +net_send_tcp_packet6(int payload_len, int dport, int sport, u8 action, + u32 tcp_seq_num, u32 tcp_ack_num) +{ + return -1; +} + static inline int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) diff --git a/net/Makefile b/net/Makefile index 3e2d061338..002b0f68a2 100644 --- a/net/Makefile +++ b/net/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_TCP_FUNCTION_FASTBOOT) += fastboot_tcp.o obj-$(CONFIG_CMD_WOL) += wol.o obj-$(CONFIG_PROT_UDP) += udp.o obj-$(CONFIG_PROT_TCP) += tcp.o +obj-$(CONFIG_IPV6) += tcp6.o obj-$(CONFIG_CMD_WGET) += wget.o
# Disable this warning as it is triggered by: diff --git a/net/net.c b/net/net.c index 75a2bbaee3..468f7d924f 100644 --- a/net/net.c +++ b/net/net.c @@ -92,6 +92,7 @@ #include <log.h> #include <net.h> #include <net6.h> +#include <net/tcp6.h> #include <ndisc.h> #include <net/fastboot_udp.h> #include <net/fastboot_tcp.h> @@ -386,6 +387,8 @@ static void net_clear_handlers(void) net_set_udp_handler(NULL); net_set_arp_handler(NULL); net_set_timeout_handler(0, NULL); + if (IS_ENABLED(CONFIG_IPV6) && IS_ENABLED(CONFIG_PROT_TCP)) + net_set_tcp_handler6(NULL); }
static void net_cleanup_loop(void) @@ -1644,10 +1647,10 @@ int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot) } }
-void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source, +void net_set_ip_header(uchar *pkt_ip_hdr, struct in_addr dest, struct in_addr source, u16 pkt_len, u8 proto) { - struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt; + struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt_ip_hdr;
/* * Construct an IP header. diff --git a/net/net6.c b/net/net6.c index 2dd64c0e16..07941156ac 100644 --- a/net/net6.c +++ b/net/net6.c @@ -14,6 +14,7 @@ #include <malloc.h> #include <net.h> #include <net6.h> +#include <net/tcp6.h> #include <ndisc.h>
/* NULL IPv6 address */ @@ -324,15 +325,13 @@ int ip6_add_hdr(uchar *xip, struct in6_addr *src, struct in6_addr *dest, return sizeof(struct ip6_hdr); }
-int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, - int sport, int len) +int udp6_add_hdr(uchar *xip, struct in6_addr *dest, int dport, int sport, + int len) { - uchar *pkt; struct udp_hdr *udp; u16 csum_p;
- udp = (struct udp_hdr *)((uchar *)net_tx_packet + net_eth_hdr_size() + - IP6_HDR_SIZE); + udp = (struct udp_hdr *)xip;
udp->udp_dst = htons(dport); udp->udp_src = htons(sport); @@ -344,44 +343,102 @@ int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, udp->udp_xsum = csum_ipv6_magic(&net_ip6, dest, len + UDP_HDR_SIZE, IPPROTO_UDP, csum_p);
+ return sizeof(struct udp_hdr); +} + +int net_send_ip_packet6(uchar *ether, struct in6_addr *dest, int dport, int sport, + int payload_len, int proto, u8 action, u32 tcp_seq_num, + u32 tcp_ack_num) +{ + uchar *pkt; + int pkt_hdr_size; + int eth_hdr_size; + int ip_hdr_size; + int udp_hdr_size; + int tcp_hdr_size; + + if (!net_tx_packet) + return -1; + + if ((proto == IPPROTO_UDP && !IS_ENABLED(CONFIG_PROT_UDP)) || + (proto == IPPROTO_TCP && !IS_ENABLED(CONFIG_PROT_TCP))) + return -EINVAL; + + pkt = (uchar *)net_tx_packet; + + eth_hdr_size = net_set_ether(pkt, ether, PROT_IP6); + pkt_hdr_size = eth_hdr_size; + pkt += eth_hdr_size; + + switch (proto) { + case IPPROTO_UDP: + ip_hdr_size = ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, + payload_len + UDP_HDR_SIZE); + pkt_hdr_size += ip_hdr_size; + pkt += ip_hdr_size; + + udp_hdr_size = udp6_add_hdr(pkt, dest, dport, sport, payload_len); + pkt_hdr_size += udp_hdr_size; + pkt += udp_hdr_size; + break; + case IPPROTO_TCP: + tcp_hdr_size = net_set_tcp_header6(pkt, dport, sport, + payload_len, action, tcp_seq_num, + tcp_ack_num); + ip_hdr_size = ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_TCP, 64, + tcp_hdr_size + payload_len); + + pkt_hdr_size += ip_hdr_size + tcp_hdr_size; + pkt += ip_hdr_size + tcp_hdr_size; + break; + default: + return -EINVAL; + } + /* if MAC address was not discovered yet, save the packet and do * neighbour discovery */ - if (!memcmp(ether, net_null_ethaddr, 6)) { + if (memcmp(ether, net_null_ethaddr, 6) == 0) { + memcpy((uchar *)net_nd_tx_packet, + (uchar *)net_tx_packet, pkt_hdr_size + payload_len); + memset((uchar *)net_tx_packet, 0, pkt_hdr_size + payload_len); + net_copy_ip6(&net_nd_sol_packet_ip6, dest); net_nd_packet_mac = ether; - - pkt = net_nd_tx_packet; - pkt += net_set_ether(pkt, net_nd_packet_mac, PROT_IP6); - pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, - len + UDP_HDR_SIZE); - memcpy(pkt, (uchar *)udp, len + UDP_HDR_SIZE); - /* size of the waiting packet */ - net_nd_tx_packet_size = (pkt - net_nd_tx_packet) + - UDP_HDR_SIZE + len; - - /* and do the neighbor solicitation */ + net_nd_tx_packet_size = pkt_hdr_size + payload_len; net_nd_try = 1; net_nd_timer_start = get_timer(0); ndisc_request(); return 1; /* waiting */ }
- pkt = (uchar *)net_tx_packet; - pkt += net_set_ether(pkt, ether, PROT_IP6); - pkt += ip6_add_hdr(pkt, &net_ip6, dest, IPPROTO_UDP, 64, - len + UDP_HDR_SIZE); - (void)eth_send(net_tx_packet, pkt - net_tx_packet + UDP_HDR_SIZE + len); + (void)eth_send(net_tx_packet, pkt_hdr_size + payload_len);
return 0; /* transmitted */ }
+int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, + int sport, int len) +{ + return net_send_ip_packet6(ether, dest, dport, sport, len, IPPROTO_UDP, 0, 0, 0); +} + +int net_send_tcp_packet6(int payload_len, int dport, int sport, u8 action, + u32 tcp_seq_num, u32 tcp_ack_num) +{ + return net_send_ip_packet6(net_server_ethaddr, &net_server_ip6, dport, + sport, payload_len, IPPROTO_TCP, action, + tcp_seq_num, tcp_ack_num); +} + int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) { - struct in_addr zero_ip = {.s_addr = 0 }; struct icmp6hdr *icmp; + struct in_addr zero_ip = {.s_addr = 0 }; struct udp_hdr *udp; + union tcp6_build_pkt *tcp_headers; + struct tcp_hdr *tcp; u16 csum; u16 csum_p; u16 hlen; @@ -392,6 +449,10 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) if (ip6->version != 6) return -EINVAL;
+ if ((ip6->nexthdr == IPPROTO_UDP && !IS_ENABLED(CONFIG_PROT_UDP)) || + (ip6->nexthdr == IPPROTO_TCP && !IS_ENABLED(CONFIG_PROT_TCP))) + return -EINVAL; + switch (ip6->nexthdr) { case PROT_ICMPV6: icmp = (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); @@ -434,12 +495,27 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) return -EINVAL;
/* IP header OK. Pass the packet to the current handler. */ - net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE + - UDP_HDR_SIZE, - ntohs(udp->udp_dst), - zero_ip, - ntohs(udp->udp_src), - ntohs(udp->udp_len) - 8); + net_get_udp_handler()((uchar *)ip6 + IP6_HDR_SIZE + UDP_HDR_SIZE, + ntohs(udp->udp_dst), + zero_ip, + ntohs(udp->udp_src), + ntohs(udp->udp_len) - 8); + break; + case IPPROTO_TCP: + tcp = (struct tcp_hdr *)(((uchar *)ip6) + IP6_HDR_SIZE); + csum = tcp->tcp_xsum; + hlen = ntohs(ip6->payload_len); + tcp->tcp_xsum = 0; + /* checksum */ + csum_p = csum_partial((u8 *)tcp, hlen, 0); + tcp->tcp_xsum = csum_ipv6_magic(&ip6->saddr, &ip6->daddr, + hlen, IPPROTO_TCP, csum_p); + + if (csum != tcp->tcp_xsum) + return -EINVAL; + + tcp_headers = (union tcp6_build_pkt *)ip6; + rxhand_tcp6(tcp_headers, len); break; default: return -EINVAL; diff --git a/net/tcp6.c b/net/tcp6.c new file mode 100644 index 0000000000..b6fd49da2f --- /dev/null +++ b/net/tcp6.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 The Android Open Source Project + */ + +#include <common.h> +#include <net/tcp.h> +#include <net/tcp6.h> +#include <net6.h> + +static rxhand_tcp6_f *tcp6_packet_handler; + +static void dummy_handler(uchar *pkt, u16 dport, + struct in6_addr sip, u16 sport, + u32 tcp_seq_num, u32 tcp_ack_num, + u8 action, unsigned int len) +{ +} + +void net_set_tcp_handler6(rxhand_tcp6_f *f) +{ + if (!f) + tcp6_packet_handler = dummy_handler; + else + tcp6_packet_handler = f; +} + +int net_set_tcp_header6(uchar *pkt_ip_hdr, u16 dport, u16 sport, int payload_len, + u8 action, u32 tcp_seq_num, u32 tcp_ack_num) +{ + union tcp6_build_pkt *b = (union tcp6_build_pkt *)pkt_ip_hdr; + int tcp_hdr_len; + int pkt_len; + u16 csum; + + pkt_len = IP6_HDR_SIZE; + tcp_hdr_len = net_set_tcp_header_common(&b->ip.tcp_hdr, &b->ip.tcp_o, + &b->sack.t_opt, &b->sack.sack_v, + dport, sport, payload_len, action, + tcp_seq_num, tcp_ack_num); + pkt_len += tcp_hdr_len; + pkt_len += payload_len; + + csum = csum_partial((u8 *)&b->ip.tcp_hdr, tcp_hdr_len + payload_len, 0); + b->ip.tcp_hdr.tcp_xsum = csum_ipv6_magic(&net_ip6, &net_server_ip6, + tcp_hdr_len + payload_len, + IPPROTO_TCP, csum); + + return tcp_hdr_len; +} + +void rxhand_tcp6(union tcp6_build_pkt *b, unsigned int len) +{ + int tcp_len = len - IP6_HDR_SIZE; + u8 tcp_action = TCP_DATA; + u32 tcp_seq_num, tcp_ack_num; + u32 res_tcp_seq_num, res_tcp_ack_num; + int tcp_hdr_len, payload_len; + + net_copy_ip6(&net_server_ip6, &b->ip.ip_hdr.saddr); + + tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.tcp_hdr.tcp_hlen); + payload_len = tcp_len - tcp_hdr_len; + + if (tcp_hdr_len > TCP_HDR_SIZE) + tcp_parse_options((uchar *)b + IP6_HDR_SIZE + TCP_HDR_SIZE, + tcp_hdr_len - TCP_HDR_SIZE); + + tcp_seq_num = ntohl(b->ip.tcp_hdr.tcp_seq); + tcp_ack_num = ntohl(b->ip.tcp_hdr.tcp_ack); + + tcp_action = tcp_state_machine(b->ip.tcp_hdr.tcp_flags, + tcp_seq_num, &res_tcp_seq_num, &res_tcp_ack_num, + payload_len); + + if ((tcp_action & TCP_PUSH) || payload_len > 0) { + debug_cond(DEBUG_DEV_PKT, + "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n", + tcp_action, tcp_seq_num, tcp_ack_num, payload_len); + + (*tcp6_packet_handler) ((uchar *)b + len - payload_len, b->ip.tcp_hdr.tcp_dst, + b->ip.ip_hdr.saddr, b->ip.tcp_hdr.tcp_src, tcp_seq_num, + tcp_ack_num, tcp_action, payload_len); + + } else if (tcp_action != TCP_DATA) { + debug_cond(DEBUG_DEV_PKT, + "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n", + tcp_action, res_tcp_seq_num, res_tcp_ack_num, payload_len); + + net_send_tcp_packet6(0, ntohs(b->ip.tcp_hdr.tcp_src), + ntohs(b->ip.tcp_hdr.tcp_dst), + (tcp_action & (~TCP_PUSH)), + res_tcp_seq_num, res_tcp_ack_num); + } +} + +// #endif