Add TCP/IP6 related headers and reuse refactored TCP/IP
implementation

Signed-off-by: Dmitrii Merkurev <dimori...@google.com>
Cc: Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
Cc: Simon Glass <s...@chromium.org>
Сс: Joe Hershberger <joe.hershber...@ni.com>
Сс: Ramon Fried <rfried....@gmail.com>
Reviewed-by: Ying-Chun Liu (PaulLiu) <paul....@linaro.org>
Reviewed-by: Simon Glass <s...@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
-- 
2.41.0.487.g6d72f3e995-goog

Reply via email to