
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 --- include/net/tcp6.h | 103 +++++++++++++++++++++++++++++++++++++++++++++ include/net6.h | 14 ++++++ net/Makefile | 1 + net/net.c | 6 +++ net/net6.c | 43 ++++++++++++++++++- net/tcp6.c | 95 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 2 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..102f5a240a --- /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() - set + * @pkt: pointer to IP6/TCP headers + * @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, 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..fa926f07ac 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 * 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 0b68bf7b13..3fc6681898 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,9 @@ static void net_clear_handlers(void) net_set_udp_handler(NULL); net_set_arp_handler(NULL); net_set_timeout_handler(0, NULL); +#if defined(CONFIG_IPV6) + net_set_tcp_handler6(NULL); +#endif }
static void net_cleanup_loop(void) @@ -916,7 +920,9 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport, { uchar *pkt; int eth_hdr_size; +#if defined(CONFIG_PROT_TCP) int ip_tcp_hdr_size; +#endif int pkt_hdr_size;
/* make sure the net_tx_packet is initialized (net_init() was called) */ diff --git a/net/net6.c b/net/net6.c index e395b930b0..6418611403 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 */ @@ -362,8 +363,8 @@ int net_send_ip_packet6(uchar *ether, struct in6_addr *dest, int dport, int spor 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; + pkt_hdr_size = eth_hdr_size; + pkt += eth_hdr_size;
switch (proto) { #if defined(CONFIG_PROT_UDP) @@ -377,6 +378,18 @@ int net_send_ip_packet6(uchar *ether, struct in6_addr *dest, int dport, int spor pkt_hdr_size += udp_hdr_size; pkt += udp_hdr_size; break; +#endif +#if defined(CONFIG_PROT_TCP) + 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; #endif default: return -EINVAL; @@ -411,11 +424,21 @@ int net_send_udp_packet6(uchar *ether, struct in6_addr *dest, int dport, 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) { + union tcp6_build_pkt *tcp_headers; struct in_addr zero_ip = {.s_addr = 0 }; struct icmp6hdr *icmp; struct udp_hdr *udp; + struct tcp_hdr *tcp; u16 csum; u16 csum_p; u16 hlen; @@ -475,6 +498,22 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len) 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..9add8cddab --- /dev/null +++ b/net/tcp6.c @@ -0,0 +1,95 @@ +// 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, 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; + 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); + } +}