/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_post.c,v $ $Revision: 1.8.10.9 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_post.c,v $ * $Author: hxu0 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * "Power On Self Test" for the network stack. This module contains a * test-cases that the target will run in the background right after * boot to test the status of the stack. The tests here is by no means * a full conformance test suite, they perform some basic sanity testing * to see if the stack might be in OK. * * All tests are run in a virtual router that cannot be used for * anything else. Other applications on the target will not "see" the * configurations used here. The tests does use RAM and CPU, so they * might still interfere with other applications. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPNET_USE_POST #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include "ipnet_h.h" #include "ipnet_sockdev.h" #include "ipnet_ip4.h" #include "ipnet_ip6.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPNET_POST_FAILED() ipnet_post_failed(__LINE__) #define RANGE_END ((Ip_size_t) -1) /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipnet_payload_ops_struct { void (*dtor)(void *payload); Ip_size_t (*len)(void *payload); Ip_u32 (*csum)(void *payload, Ip_u32 partial_csum); void (*serialize)(void *payload, Ip_size_t *offset, Ip_size_t *range); } Ipnet_payload_ops; typedef struct Ipnet_payload_struct { Ipnet_payload_ops *ops; struct Ipnet_payload_struct *next; int type; /* * Buffer where the encoded IP packet is stored. */ struct Ip_iovec iov; } Ipnet_payload_t; /* * Represents generic data payload. The data is copied verbatim * into the created packet. */ typedef struct Ipnet_payload_data_struct { Ipnet_payload_t payload; void *data; Ip_size_t datalen; } Ipnet_payload_data_t; #ifdef IPCOM_USE_INET typedef struct Ipnet_payload_ip_struct { Ipnet_payload_t payload; Ip_u16 fragment_offset; Ip_size_t plen; Ipnet_pkt_ip ip; Ip_u8 ipopts[40]; Ip_size_t ipoptslen; } Ipnet_payload_ip_t; typedef struct Ipnet_payload_icmp_struct { Ipnet_payload_t payload; Ipnet_pkt_icmp *icmp; Ip_size_t icmplen; } Ipnet_payload_icmp_t; #define ICMP_T_ECHO_REQ 8 #define ICMP_T_ECHO_RES 0 #define ICMP_T_PARM_PROB 12 #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 typedef struct Ipnet_payload_ip6_struct { Ipnet_payload_t payload; Ip_u16 fragment_offset; /* Payload length excluding fragmentation header */ Ip_u16 plen; Ipnet_pkt_ip6 ip6; Ip_pkt_ip6_frag frag; } Ipnet_payload_ip6_t; typedef struct Ipnet_payload_ip6ext_struct { Ipnet_payload_t payload; /* * Number of 8-octet bundles and '0' means 8-octets, '1' means * 16-octets etc. */ Ip_pkt_ip6_ext_hdr *ext; Ip_u64 extbuf; } Ipnet_payload_ip6ext_t; typedef struct Ipnet_payload_icmp6_struct { Ipnet_payload_t payload; Ipnet_pkt_icmp6 *icmp; Ip_size_t icmplen; } Ipnet_payload_icmp6_t; #define ICMP6_T_PARAM_PROB 4 #define ICMP6_T_ECHO_REQ 128 #define ICMP6_T_ECHO_REP 129 #define ICMP6_T_RT_ADV 134 #endif /* IPCOM_USE_INET6 */ #include "ipcom_align16.h" typedef IP_PACKED1 struct Ipnet_pkt_tcp_struct { Ip_u16 sport; Ip_u16 dport; Ip_u32 seq_num; Ip_u32 ack_num; Ip_u16 offset_flags; Ip_u16 window; Ip_u16 chksum; Ip_u16 urgent_pointer; } IP_PACKED2 Ipnet_pkt_tcp; #include "ipcom_align16.h" #define TCP_F_URG ip_htons(0x0020) #define TCP_F_ACK ip_htons(0x0010) #define TCP_F_PSH ip_htons(0x0008) #define TCP_F_RST ip_htons(0x0004) #define TCP_F_SYN ip_htons(0x0002) #define TCP_F_FIN ip_htons(0x0001) typedef struct Ipnet_payload_tcp_struct { Ipnet_payload_t payload; Ipnet_pkt_tcp tcp; } Ipnet_payload_tcp_t; typedef struct Ipnet_payload_udp_struct { Ipnet_payload_t payload; Ip_in_port_t sport; Ip_in_port_t dport; Ip_u16 csum; Ipnet_pkt_udp udp; } Ipnet_payload_udp_t; typedef struct Ipnet_post_session_struct { int dev0_sock; char dev0_ifname[IP_IFNAMSIZ]; int dev1_sock; char dev1_ifname[IP_IFNAMSIZ]; } Ipnet_post_session_t; typedef struct Ipnet_post_addr_pair_struct { /* * Address assigned to the sockdev interface */ const char *local; /* * Address of the imaginary node that is simulated by the test. */ const char *remote; } Ipnet_post_addr_pair_t; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* * The VR index used for all POST testing. */ int vr = 60001; /* * The local address is the address assigned to the sockdev interface, * the remote address is the imaginary node that the test simulates. */ Ipnet_post_addr_pair_t inet_addrs[] = { { "192.168.1.1", "192.168.1.2" }, { "192.168.2.1", "192.168.2.2" }, }; Ipnet_post_addr_pair_t inet6_addrs[] = { { "fc01::1", "fc01::2" }, { "fc02::1", "fc02::2" }, }; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_post_failed *=========================================================================== * Description: Called if any of power on self tests fails. * Parameters: * Returns: * */ IP_STATIC void ipnet_post_failed(int line) { char msg[40]; ipcom_sprintf(msg, "Power On Self Test FAILED at line %d!\n", line); IPCOM_LOG0(CRIT, msg); ipcom_debug_printf(msg); ipcom_sleep(1); IP_PANIC(); } /* *=========================================================================== * ipnet_post_get_local_port *=========================================================================== * Description: Returns the local port the socket is bound to. * Parameters: sock - an UDP or TCP socket * Returns: The local port. * */ IP_STATIC Ip_in_port_t ipnet_post_get_local_port(int sock) { struct Ip_sockaddr_storage ss; Ip_socklen_t ss_len = sizeof(ss); if (ipcom_getsockname(sock, (struct Ip_sockaddr *)&ss, &ss_len) < 0) IPNET_POST_FAILED(); /* * This happens to work for both AF_INET and AF_INET6 sockets * since the port is stored at the same offset. */ return ip_ntohs(((struct Ip_sockaddr_in*) &ss)->sin_port); } /* *=========================================================================== * ipnet_post_connect_socket *=========================================================================== * Description: Connects a socket * Parameters: sock - the socket to connect * addr - an IP-adress * port - TCP/UDP port * non_block - IP_TRUE if the connect should be non_blocking. * Returns: * */ IP_STATIC void ipnet_post_connect_socket(int sock, const char *addr, Ip_in_port_t port, Ip_bool non_block) { struct Ip_addrinfo hints; struct Ip_addrinfo *res; char str_port[6]; Ip_socklen_t optlen; ipcom_sprintf(str_port, "%d", port); ipcom_memset(&hints, 0, sizeof(hints)); hints.ai_flags = IP_AI_NUMERICHOST; optlen = sizeof(hints.ai_socktype); if (ipcom_getsockopt(sock, IP_SOL_SOCKET, IP_SO_TYPE, &hints.ai_socktype, &optlen) < 0) IPNET_POST_FAILED(); ipcom_getaddrinfo(addr, str_port, &hints, &res); if (ipcom_socketioctl(sock, IP_FIONBIO, &non_block) < 0) IPNET_POST_FAILED(); if (ipcom_connect(sock, res->ai_addr, (Ip_socklen_t)res->ai_addrlen) < 0) { if (!non_block || ipcom_errno != IP_ERRNO_EINPROGRESS) IPNET_POST_FAILED(); } non_block = IP_FALSE; if (ipcom_socketioctl(sock, IP_FIONBIO, &non_block) < 0) IPNET_POST_FAILED(); ipcom_freeaddrinfo(res); } IP_STATIC void ipnet_post_app_sock_delete(int psock) { struct Ip_linger linger = { 1, 0 }; (void)ipcom_setsockopt(psock, IP_SOL_SOCKET, IP_SO_LINGER, &linger, sizeof(linger)); ipcom_socketclose(psock); } IP_STATIC int ipnet_post_app_sock_new(struct Ip_addrinfo *hints, const char *addr, const char *service) { struct Ip_timeval tv = { 10, 0 }; struct Ip_addrinfo *ai; int sock; if (ipcom_getaddrinfo(addr, service, hints, &ai) != 0) IPNET_POST_FAILED(); sock = ipcom_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) IPNET_POST_FAILED(); if (ipcom_setsockopt(sock, IP_SOL_SOCKET, IP_SO_X_VR, &vr, sizeof(vr)) < 0) IPNET_POST_FAILED(); if (ipcom_setsockopt(sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &tv, sizeof(tv)) < 0) IPNET_POST_FAILED(); if (ipcom_bind(sock, ai->ai_addr, (Ip_socklen_t)ai->ai_addrlen) < 0) IPNET_POST_FAILED(); ipcom_freeaddrinfo(ai); return sock; } IP_STATIC int ipnet_post_udp_session_new(const char *local_addr, Ip_in_port_t local_port) { struct Ip_addrinfo hints; char str_port[6]; ipcom_sprintf(str_port, "%d", local_port); ipcom_memset(&hints, 0, sizeof(hints)); hints.ai_flags = IP_AI_NUMERICHOST; hints.ai_socktype = IP_SOCK_DGRAM; return ipnet_post_app_sock_new(&hints, local_addr, str_port); } IP_STATIC int ipnet_post_tcp_session_new(const char *local_addr, Ip_in_port_t local_port) { struct Ip_addrinfo hints; char str_port[6]; ipcom_sprintf(str_port, "%d", local_port); ipcom_memset(&hints, 0, sizeof(hints)); hints.ai_flags = IP_AI_NUMERICHOST; hints.ai_socktype = IP_SOCK_STREAM; return ipnet_post_app_sock_new(&hints, local_addr, str_port); } #ifdef IPCOM_USE_INET6 IP_STATIC int ipnet_post_icmp6_sock_new(const char *laddr) { struct Ip_addrinfo hints; ipcom_memset(&hints, 0, sizeof(hints)); hints.ai_flags = IP_AI_NUMERICHOST; hints.ai_family = IP_AF_INET6; hints.ai_socktype = IP_SOCK_RAW; hints.ai_protocol = IP_IPPROTO_ICMPV6; return ipnet_post_app_sock_new(&hints, laddr, IP_NULL); } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * ipnet_post_do_vr_op *=========================================================================== * Description: Creates VRs that will be used during testing. * Parameters: add_vrs - IP_TRUE if the test VRs should be added * IP_FALSE if the test VRs should be delted * Returns: * */ IP_STATIC void ipnet_post_do_vr_op(Ip_bool add_vrs) { int sock; unsigned long request = add_vrs ? IP_SIOCADDVR :IP_SIOCDELVR; unsigned short arg; #ifdef IPCOM_USE_INET6 sock = ipcom_socket(IP_AF_INET6, IP_SOCK_DGRAM, 0); #else sock = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0); #endif arg = vr & 0x0000ffff; if (ipcom_socketioctl(sock, request, &arg) < 0) IPNET_POST_FAILED(); ipcom_socketclose(sock); } /* *=========================================================================== * ipnet_post_sockaddr_from_str *=========================================================================== * Description: Converts a string representation of an IPv4 address * into a socket address structure. * Parameters: addr - a string representation of an IPv4 address. * sa - Socket address matching the passed IPv4 address. * Returns: * */ static void ipnet_post_sockaddr_from_str(const char *addr, struct Ip_sockaddr *sa) { struct Ip_sockaddr_in *sin = (struct Ip_sockaddr_in *) sa; ipcom_memset(sin, 0, sizeof(sa)); IPCOM_SA_LEN_SET(sin, sizeof(sa)); sin->sin_family = IP_AF_INET; if (ipcom_inet_pton(IP_AF_INET, addr, &sin->sin_addr) != 1) { /* * 'addr' is not a valid IPv4 address */ IPNET_POST_FAILED(); } } /* *=========================================================================== * ipnet_post_sockaddr_in6_from_str *=========================================================================== * Description: Converts a string representation of an IPv6 address * into a socket address structure. * Parameters: addr - a string representation of an IPv6 address. * sin6 - Socket address matching the passed IPv6 address. * Returns: * */ IP_STATIC void ipnet_post_sockaddr_in6_from_str(const char *addr, struct Ip_sockaddr_in6 *sin6) { ipcom_memset(sin6, 0, sizeof(*sin6)); IPCOM_SA_LEN_SET(sin6, sizeof(*sin6)); sin6->sin6_family = IP_AF_INET6; if (ipcom_inet_pton(IP_AF_INET6, addr, &sin6->sin6_addr) != 1) { /* * 'addr' is not a valid IPv6 address */ IPNET_POST_FAILED(); } } IP_STATIC int ipnet_post_dev_sock_open(int dev_idx, char *ifname) { struct Ip_in6_aliasreq aliasreq; struct Ip_ifreq ifreq; int dev_sock; int sndbuf = 70000; struct Ip_timeval tv = { 10, 0 }; dev_sock = ipcom_socket(IP_AF_SOCKDEV, IP_SOCK_RAW, dev_idx); ipcom_sprintf(ifname, IPNET_SOCKDEV_IF_NAME_BASE "%d", dev_idx); ipcom_strcpy(ifreq.ifr_name, ifname); ifreq.ip_ifr_vr = (Ip_u16)vr; if (ipcom_socketioctl(dev_sock, IP_SIOCSIFVR, &ifreq) < 0) IPNET_POST_FAILED(); if (ipcom_setsockopt(dev_sock, IP_SOL_SOCKET, IP_SO_X_VR, &vr, sizeof(vr)) < 0) IPNET_POST_FAILED(); /* * The send buffer must be large enough to handle the case where * IP-datagrams slighly larger than 64k is sent. */ if (ipcom_setsockopt(dev_sock, IP_SOL_SOCKET, IP_SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) IPNET_POST_FAILED(); if (ipcom_setsockopt(dev_sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &tv, sizeof(tv)) < 0) IPNET_POST_FAILED(); #ifdef IPCOM_USE_INET ipnet_post_sockaddr_from_str(inet_addrs[dev_idx].local, &ifreq.ip_ifr_addr); if (ipcom_socketioctl(dev_sock, IP_SIOCSIFADDR, &ifreq) < 0) IPNET_POST_FAILED(); #endif #ifdef IPCOM_USE_INET6 ipcom_memset(&aliasreq, 0, sizeof(aliasreq)); ipcom_strcpy(aliasreq.ifra_name, ifreq.ifr_name); ipnet_post_sockaddr_in6_from_str(inet6_addrs[dev_idx].local, &aliasreq.ifra_addr); ipnet_post_sockaddr_in6_from_str("ffff:ffff:ffff:ffff::", &aliasreq.ifra_prefixmask); aliasreq.ifra_lifetime.ia6t_expire = IPCOM_ADDR_INFINITE; aliasreq.ifra_lifetime.ia6t_preferred = IPCOM_ADDR_INFINITE; if (ipcom_socketioctl(dev_sock, IP_SIOCAIFADDR_IN6, &aliasreq) < 0) IPNET_POST_FAILED(); #endif return dev_sock; } /* *=========================================================================== * ipnet_post_dev_flush *=========================================================================== * Description: Discards all IP datagrams that has been sent to the * socket device. * Parameters: dev_sock - a socket network device. * Returns: * */ IP_STATIC void ipnet_post_dev_flush(int dev_sock) { while (ipcom_recv(dev_sock, IP_NULL, 0, IP_MSG_DONTWAIT) == 0) /* Discard segment*/; } /* * Generic payload functions */ IP_STATIC void * ipnet_payload_append(void *container, void *payload) { Ipnet_payload_t *c = container; c->next = payload; return c; } IP_STATIC void ipnet_payload_set_iov(struct Ip_iovec *iov, void *base, Ip_size_t len, Ip_size_t *offset, Ip_size_t *range) { iov->iov_base = base; iov->iov_len = len; if (*offset > iov->iov_len) { /* * This payload is not included, it is sittning in front of * the included window. */ *offset -= iov->iov_len; iov->iov_len = 0; return; } iov->iov_base = (char *) iov->iov_base + *offset; iov->iov_len -= *offset; *offset = 0; if (iov->iov_len < *range) { /* * This iov should be fully included. */ *range -= iov->iov_len; return; } iov->iov_len = *range; *range = 0; } IP_STATIC void ipnet_payload_delete_all(void *payload) { Ipnet_payload_t *p = payload; if (p->next) ipnet_payload_delete_all(p->next); p->ops->dtor(p); } IP_STATIC Ip_size_t ipnet_payload_len_all(void *payload) { Ipnet_payload_t *p = payload; if (p == IP_NULL) return 0; return p->ops->len(p) + ipnet_payload_len_all(p->next); } IP_STATIC Ip_size_t ipnet_next_payload_len(void *payload) { Ipnet_payload_t *p = payload; return ipnet_payload_len_all(p->next); } IP_STATIC void ipnet_payload_serialize_all(Ipnet_payload_t *p, Ip_size_t *offset, Ip_size_t *range) { if (p) { p->ops->serialize(p, offset, range); ipnet_payload_serialize_all(p->next, offset, range); } } IP_STATIC void ipnet_payload_make_iov(Ipnet_payload_t *p, struct Ip_iovec *iov, Ip_size_t iovlen) { if (p) { iov[iovlen] = p->iov; ipnet_payload_make_iov(p->next, iov, iovlen + 1); } } IP_STATIC Ip_size_t ipnet_payload_iovlen(Ipnet_payload_t *p) { if (p == IP_NULL) return 0; return 1 + ipnet_payload_iovlen(p->next); } IP_STATIC void ipnet_payload_send(void *payload, int sock) { Ipnet_payload_t *p = payload; struct Ip_msghdr m; struct Ip_iovec *iov; Ip_size_t iovlen; Ip_size_t offset; Ip_size_t range; ipnet_payload_serialize_all(p, &offset, &range); iovlen = ipnet_payload_iovlen(p); iov = ipcom_calloc(iovlen, sizeof(*iov)); ipnet_payload_make_iov(p, iov, 0); ipcom_memset(&m, 0, sizeof(m)); m.msg_iov = iov; m.msg_iovlen = (int)iovlen; if (ipcom_sendmsg(sock, &m, 0) < 0) IPNET_POST_FAILED(); ipcom_free(iov); } /* * Data payload functions. The currently implementation assumes that * nothing follows this payload. */ IP_STATIC void ipnet_payload_data_dtor(void *payload) { Ipnet_payload_data_t *p = payload; ipcom_free(p->data); ipcom_free(p); } IP_STATIC Ip_size_t ipnet_payload_data_len(void *payload) { Ipnet_payload_data_t *p = payload; return p->datalen; } IP_STATIC void ipnet_payload_data_set_len(Ipnet_payload_data_t *p, Ip_size_t sz) { ipcom_free(p->data); p->data = ipcom_malloc(sz); p->datalen = sz; } IP_STATIC Ip_u32 ipnet_payload_data_csum(void *payload, Ip_u32 partial_sum) { Ipnet_payload_data_t *p = payload; return partial_sum + ipcom_in_checksum_update(p->data, p->datalen); } IP_STATIC void ipnet_payload_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_data_t *p = payload; ipnet_payload_set_iov(&p->payload.iov, p->data, p->datalen, offset, range); } IP_STATIC Ipnet_payload_data_t * ipnet_payload_data_new(Ip_size_t sz) { static Ipnet_payload_ops ops = { ipnet_payload_data_dtor, ipnet_payload_data_len, ipnet_payload_data_csum, ipnet_payload_serialize, }; Ipnet_payload_data_t *p; Ip_size_t i; char *data; p = ipcom_calloc(1, sizeof(*p)); p->payload.ops = &ops; ipnet_payload_data_set_len(p, sz); /* * Initialize the data */ data = p->data; for (i = 0; i < sz; i++) data[i] = (char)i; return p; } /* * TCP payload functions. */ IP_STATIC void ipnet_payload_tcp_dtor(void *payload) { ipcom_free(payload); } IP_STATIC Ip_size_t ipnet_payload_tcp_len(void *payload) { IPCOM_UNUSED_ARG(payload); return 20; } IP_STATIC void ipnet_payload_tcp_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_tcp_t *p = payload; ipnet_payload_set_iov(&p->payload.iov, &p->tcp, ipnet_payload_tcp_len(p), offset, range); } IP_STATIC Ip_u32 ipnet_payload_tcp_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_tcp_t *p = payload; Ipnet_payload_t *next_p = p->payload.next; Ip_u32 csum = partial_csum; p->tcp.chksum = 0; csum += ipcom_in_checksum_update(&p->tcp, ipnet_payload_tcp_len(p)); if (next_p) csum = next_p->ops->csum(next_p, csum); p->tcp.chksum = ipcom_in_checksum_finish(csum); return csum; } IP_STATIC Ipnet_payload_tcp_t * ipnet_payload_tcp_new(Ip_in_port_t sport, Ip_in_port_t dport) { static Ipnet_payload_ops ops = { ipnet_payload_tcp_dtor, ipnet_payload_tcp_len, ipnet_payload_tcp_csum, ipnet_payload_tcp_serialize }; Ipnet_payload_tcp_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_IPPROTO_TCP; p->payload.ops = &ops; p->tcp.sport = ip_htons(sport); p->tcp.dport = ip_htons(dport); p->tcp.offset_flags = ip_htons(0x5000); p->tcp.window = ip_htons(0x1234); return p; } IP_STATIC void ipnet_payload_tcp_flags(Ipnet_payload_tcp_t *t, Ip_u16 flags_n) { t->tcp.offset_flags &= ip_htons(0xf000); t->tcp.offset_flags |= flags_n; } IP_STATIC void ipnet_payload_tcp_seq(Ipnet_payload_tcp_t *t, Ip_u32 seq) { t->tcp.seq_num = ip_htonl(seq); } IP_STATIC void ipnet_payload_tcp_ack(Ipnet_payload_tcp_t *t, Ip_u32 ack) { t->tcp.ack_num = ip_htonl(ack); } /* * UDP payload functions. */ IP_STATIC void ipnet_payload_udp_dtor(void *payload) { ipcom_free(payload); } IP_STATIC Ip_size_t ipnet_payload_udp_len(void *payload) { IPCOM_UNUSED_ARG(payload); return 8; } IP_STATIC void ipnet_payload_udp_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_udp_t *p = payload; ipnet_payload_set_iov(&p->payload.iov, &p->udp, ipnet_payload_udp_len(p), offset, range); } IP_STATIC Ip_u32 ipnet_payload_udp_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_udp_t *p = payload; Ipnet_payload_t *next_p = p->payload.next; Ip_u32 csum = partial_csum; p->udp.ulen = ip_htons(ipnet_payload_len_all(p)); p->udp.sum = 0; csum += ipcom_in_checksum_update(&p->udp, ipnet_payload_udp_len(p)); if (next_p) csum = next_p->ops->csum(next_p, csum); p->udp.sum = ipcom_in_checksum_finish(csum); return csum; } IP_STATIC Ipnet_payload_udp_t * ipnet_payload_udp_new(Ip_in_port_t sport, Ip_in_port_t dport) { static Ipnet_payload_ops ops = { ipnet_payload_udp_dtor, ipnet_payload_udp_len, ipnet_payload_udp_csum, ipnet_payload_udp_serialize }; Ipnet_payload_udp_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_IPPROTO_UDP; p->payload.ops = &ops; p->udp.sport = ip_htons(sport); p->udp.dport = ip_htons(dport); return p; } #ifdef IPCOM_USE_INET /* * ICMP payload functions. */ IP_STATIC void ipnet_payload_icmp_dtor(void *payload) { Ipnet_payload_icmp_t *p = payload; ipcom_free(p->icmp); ipcom_free(p); } IP_STATIC Ip_size_t ipnet_payload_icmp_len(void *payload) { Ipnet_payload_icmp_t *p = payload; return p->icmplen; } IP_STATIC void ipnet_payload_icmp_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_icmp_t *p = payload; ipnet_payload_set_iov(&p->payload.iov, p->icmp, p->icmplen, offset, range); } IP_STATIC Ip_u32 ipnet_payload_icmp_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_icmp_t *p = payload; IPCOM_UNUSED_ARG(partial_csum); p->icmp->cksum = 0; return (p->icmp->cksum = ipcom_in_checksum(p->icmp, p->icmplen)); } IP_STATIC Ipnet_payload_icmp_t * ipnet_payload_icmp_new(Ip_u8 type, Ip_u8 code, Ip_u16 r0, Ip_u16 r1, const void *data, Ip_size_t datalen) { static Ipnet_payload_ops ops = { ipnet_payload_icmp_dtor, ipnet_payload_icmp_len, ipnet_payload_icmp_csum, ipnet_payload_icmp_serialize }; Ipnet_payload_icmp_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_IPPROTO_ICMP; p->payload.ops = &ops; p->icmplen = 8 + datalen; p->icmp = ipcom_malloc(p->icmplen); p->icmp->type = type; p->icmp->code = code; p->icmp->data.echo.id = r0; p->icmp->data.echo.seq = r1; ipcom_memcpy(p->icmp->data.echo.data, data, datalen); return p; } /* * IP payload functions. */ IP_STATIC void ipnet_payload_ip_narrow(void *payload, Ip_size_t offset, Ip_size_t range); IP_STATIC void ipnet_payload_ip_dtor(void *payload) { Ipnet_payload_ip_t *p = payload; ipcom_free(p); } IP_STATIC Ip_size_t ipnet_payload_ip_len(void *payload) { Ipnet_payload_ip_t *p = payload; return sizeof(p->ip) + p->ipoptslen; } IP_STATIC void ipnet_payload_ip_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_ip_t *p = payload; if (p->plen == 0) /* Default to create a single IP datagram (not a fragment) */ ipnet_payload_ip_narrow(p, 0, RANGE_END); p->payload.ops->csum(p, 0); p->payload.iov.iov_base = &p->ip; p->payload.iov.iov_len = ipnet_payload_ip_len(p); *offset = p->fragment_offset; *range = p->plen; } IP_STATIC Ip_u32 ipnet_payload_ip_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_ip_t *p = payload; Ipnet_pkt_ip *ip; Ipnet_payload_t *next_p = p->payload.next; IPCOM_UNUSED_ARG(partial_csum); ip = &p->ip; ip->v_hl = (Ip_u8) (0x40 + (ipnet_payload_ip_len(p) >> 2)); ip->sum = 0; if (next_p) { Ipnet_pkt_ip pseudo_ip; ip->p = (Ip_u8)next_p->type; ipcom_memcpy(&pseudo_ip, ip, sizeof(pseudo_ip)); pseudo_ip.v_hl = 0; pseudo_ip.tos = 0; pseudo_ip.id = 0; pseudo_ip.off = 0; pseudo_ip.ttl = 0; pseudo_ip.len = ip_htons(ipnet_next_payload_len(p)); next_p->ops->csum(next_p, ipcom_in_checksum_update(&pseudo_ip, sizeof(pseudo_ip))); } ip->sum = ipcom_in_checksum(ip, ipnet_payload_ip_len(p)); return 0; } IP_STATIC void ipnet_payload_ip_change_id(void *payload) { Ipnet_payload_ip_t *p = payload; static Ip_u16 frag_id; p->ip.id = ++frag_id; } IP_STATIC Ipnet_payload_ip_t * ipnet_payload_ip_new(const char *src, const char *dst) { static Ipnet_payload_ops ops = { ipnet_payload_ip_dtor, ipnet_payload_ip_len, ipnet_payload_ip_csum, ipnet_payload_ip_serialize }; Ipnet_payload_ip_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_AF_INET; p->payload.ops = &ops; p->ip.ttl = 64; ipcom_inet_pton(IP_AF_INET, src, p->ip.src); ipcom_inet_pton(IP_AF_INET, dst, p->ip.dst); ipnet_payload_ip_change_id(p); return p; } IP_STATIC void ipnet_payload_ip_narrow(void *payload, Ip_size_t offset, Ip_size_t range) { Ipnet_payload_ip_t *p = payload; Ip_size_t plen; p->fragment_offset = (Ip_u16)offset; p->plen = range; p->ip.off = ip_htons(offset >> 3); if (range != RANGE_END && offset + range < ipnet_next_payload_len(p)) { p->ip.off |= IPNET_OFF_MF; plen = range; } else if (range == RANGE_END) plen = ipnet_next_payload_len(p) - offset; else plen = range; p->ip.len = (Ip_u16) (plen + ipnet_payload_ip_len(p)); p->ip.len = ip_htons(p->ip.len); } IP_STATIC void ipnet_payload_ip_set_opts(void *payload, Ip_u8 *opts, Ip_size_t optslen) { Ipnet_payload_ip_t *p = payload; ipcom_memcpy(p->ipopts, opts, optslen); p->ipoptslen = optslen; ipnet_payload_ip_narrow(p, p->fragment_offset, p->plen); } /* * Helper functions to find a specific packet. */ IP_STATIC const void * ipnet_ip_match(const void *_ip, const char *src, const char *dst, Ip_u8 ipproto) { const Ipnet_pkt_ip *ip = _ip; Ip_u32 src_n; Ip_u32 dst_n; if (IPNET_IP4_GET_VERSION(ip) == 4 && ipproto == ip->p && ipcom_inet_pton(IP_AF_INET, src, &src_n) == 1 && ipcom_inet_pton(IP_AF_INET, dst, &dst_n) == 1 && ipcom_memcmp(&src_n, ip->src, 4) == 0 && ipcom_memcmp(&dst_n, ip->dst, 4) == 0) { return (char *) ip + IPNET_IP4_GET_HDR_OCTET_LEN(ip); } return IP_NULL; } IP_STATIC const void * ipnet_icmp_match(const void *_icmp, Ip_u8 type, Ip_u8 code, void *payload, Ip_size_t payload_len) { const Ipnet_pkt_icmp *icmp = _icmp; if (icmp != IP_NULL && icmp->type == type && icmp->code == code && ipcom_memcmp(&icmp->data, payload, payload_len) == 0) return (char *)&icmp->data + payload_len; return IP_NULL; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 /* * IPv6 payload functions. */ IP_STATIC void ipnet_payload_ip6_narrow(void *payload, Ip_size_t offset, Ip_size_t range); IP_STATIC void ipnet_payload_ip6_dtor(void *payload) { Ipnet_payload_ip6_t *p = payload; ipcom_free(p); } IP_STATIC Ip_size_t ipnet_payload_ip6_len(void *payload) { Ipnet_payload_ip6_t *p = payload; Ip_size_t len = IPNET_IP6_HDR_SIZE; if (p->fragment_offset != 0 || (p->plen != 0 && p->plen != ipnet_next_payload_len(p))) len += 8; /* Fragment header */ return len; } IP_STATIC void ipnet_payload_ip6_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_ip6_t *p = payload; Ipnet_payload_t *next_p = p->payload.next; if (p->plen == 0) /* Default to create a single IP datagram (not a fragment) */ ipnet_payload_ip6_narrow(p, 0, RANGE_END); if (next_p == IP_NULL) p->ip6.nxt = IP_IPPROTO_NONE; else if (ipnet_payload_ip6_len(p) == IPNET_IP6_HDR_SIZE) p->ip6.nxt = (Ip_u8)next_p->type; else { p->ip6.nxt = IP_IPPROTO_FRAGMENT; p->frag.ip6f_nxt = (Ip_u8)next_p->type; } p->payload.ops->csum(p, 0); p->payload.iov.iov_base = &p->ip6; p->payload.iov.iov_len = ipnet_payload_ip6_len(p); *offset = p->fragment_offset; *range = p->plen; } IP_STATIC Ip_u8 ipnet_payload_ip6_l4_type(Ipnet_payload_t *p) { if (p == IP_NULL) return IP_IPPROTO_NONE; switch (p->type) { case IP_IPPROTO_HOPOPTS: case IP_IPPROTO_ROUTING: case IP_IPPROTO_FRAGMENT: case IP_IPPROTO_NONE: case IP_IPPROTO_DSTOPTS: return ipnet_payload_ip6_l4_type(p->next); } return p->type; } IP_STATIC Ip_u32 ipnet_payload_ip6_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_ip6_t *p = payload; Ipnet_payload_t *next_p = p->payload.next; IPCOM_UNUSED_ARG(partial_csum); if (next_p) { Ipnet_pkt_ip6 pseudo_ip; ipcom_memcpy(&pseudo_ip, &p->ip6, sizeof(pseudo_ip)); pseudo_ip.ver_class_flow = 0; /* * Next field must be in the low byte of 16-bit word according * to the definition of pseudo-header in RFC2460. */ pseudo_ip.hlim = ipnet_payload_ip6_l4_type(next_p); pseudo_ip.nxt = 0; pseudo_ip.plen = ip_htons(ipnet_next_payload_len(p)); next_p->ops->csum(next_p, ipcom_in_checksum_update(&pseudo_ip, sizeof(pseudo_ip))); } return 0; } IP_STATIC void ipnet_payload_ip6_change_id(void *payload) { Ipnet_payload_ip6_t *p = payload; static Ip_u32 frag_id; p->frag.ip6f_ident = ++frag_id; } IP_STATIC Ipnet_payload_ip6_t * ipnet_payload_ip6_new(const char *src, const char *dst, Ip_u8 hlim) { static Ipnet_payload_ops ops = { ipnet_payload_ip6_dtor, ipnet_payload_ip6_len, ipnet_payload_ip6_csum, ipnet_payload_ip6_serialize }; Ipnet_payload_ip6_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_AF_INET6; p->payload.ops = &ops; IPNET_IP6_SET_VER(&p->ip6, 6); p->ip6.hlim = hlim; ipcom_inet_pton(IP_AF_INET6, src, &p->ip6.src); ipcom_inet_pton(IP_AF_INET6, dst, &p->ip6.dst); ipnet_payload_ip6_change_id(p); return p; } /* * IPv6 extension payload functions. */ IP_STATIC Ip_size_t ipnet_payload_ip6ext_len(void *payload) { Ipnet_payload_ip6ext_t *p = payload; return (p->ext->len + 1) * 8; } IP_STATIC void ipnet_payload_ip6ext_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_ip6ext_t *p = payload; if (p->payload.next == IP_NULL) p->ext->nxt = IP_IPPROTO_NONE; else p->ext->nxt = p->payload.next->type; ipnet_payload_set_iov(&p->payload.iov, &p->extbuf, ipnet_payload_ip6ext_len(p), offset, range); } IP_STATIC Ip_u32 ipnet_payload_ip6ext_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_ip6_t *p = payload; Ipnet_payload_t *next_p = p->payload.next; if (next_p) return next_p->ops->csum(next_p, partial_csum); return partial_csum; } IP_STATIC Ipnet_payload_ip6ext_t * ipnet_payload_ip6ext_new(Ip_u8 exttype) { static Ipnet_payload_ops ops = { ipnet_payload_ip6_dtor, /* Can use the same destructor as IP6 payloads */ ipnet_payload_ip6ext_len, ipnet_payload_ip6ext_csum, ipnet_payload_ip6ext_serialize }; Ipnet_payload_ip6ext_t *p; p = ipcom_calloc(1, sizeof(*p)); p->ext = (Ip_pkt_ip6_ext_hdr *)&p->extbuf; p->payload.type = exttype; p->payload.ops = &ops; /* The only content is NOP for now */ return p; } IP_STATIC void ipnet_payload_ip6_narrow(void *payload, Ip_size_t offset, Ip_size_t range) { Ipnet_payload_ip6_t *p = payload; p->fragment_offset = (Ip_u16)offset; if (offset == 0 && range == RANGE_END) { /* * Unfragmented packet */ p->plen = (Ip_u16) (ipnet_payload_len_all(p) - IPNET_IP6_HDR_SIZE); p->ip6.plen = ip_htons(p->plen); } else { /* * Fragmented packet */ p->frag.ip6f_offlg = ip_htons(offset); p->ip6.plen = ~0; if (range == RANGE_END) { p->plen = (Ip_u16) (ipnet_payload_len_all(p) - IPNET_IP6_HDR_SIZE - offset); p->ip6.plen = ip_htons(p->plen); } else { IP_BIT_SET(p->frag.ip6f_offlg, IP_IP6F_MORE_FRAG); p->plen = (Ip_u16)range; p->ip6.plen = ip_htons(p->plen + sizeof(Ip_pkt_ip6_frag)); } } } /* * Helper functions to find a specific packet. */ IP_STATIC const void * ipnet_ip6_match(const void *_ip, const char *_src, const char *_dst, Ip_u8 ipproto) { const Ipnet_pkt_ip6 *ip = _ip; struct Ip_in6_addr src; struct Ip_in6_addr dst; if (IPNET_IP6_GET_VER(ip) == 6 && ipproto == ip->nxt && ipcom_inet_pton(IP_AF_INET6, _src, &src) == 1 && ipcom_inet_pton(IP_AF_INET6, _dst, &dst) == 1 && ipcom_memcmp(&src, &ip->src, 16) == 0 && ipcom_memcmp(&dst, &ip->dst, 16) == 0) { return (char *) ip + IPNET_IP6_HDR_SIZE; } return IP_NULL; } IP_STATIC const void * ipnet_icmp6_match(const void *_icmp, Ip_u8 type, Ip_u8 code) { const Ipnet_pkt_icmp6 *icmp = _icmp; if (icmp != IP_NULL && icmp->icmp6_type == type && icmp->icmp6_code == code) return icmp; return IP_NULL; } /* * ICMPv6 payload functions. */ IP_STATIC void ipnet_payload_icmp6_dtor(void *payload) { Ipnet_payload_icmp6_t *p = payload; ipcom_free(p->icmp); ipcom_free(p); } IP_STATIC Ip_size_t ipnet_payload_icmp6_len(void *payload) { Ipnet_payload_icmp6_t *p = payload; return p->icmplen; } IP_STATIC void ipnet_payload_icmp6_serialize(void *payload, Ip_size_t *offset, Ip_size_t *range) { Ipnet_payload_icmp6_t *p = payload; ipnet_payload_set_iov(&p->payload.iov, p->icmp, p->icmplen, offset, range); } IP_STATIC Ip_u32 ipnet_payload_icmp6_csum(void *payload, Ip_u32 partial_csum) { Ipnet_payload_icmp6_t *p = payload; Ip_u32 csum = partial_csum; p->icmp->icmp6_cksum = 0; csum += ipcom_in_checksum_update(p->icmp, p->icmplen); return (p->icmp->icmp6_cksum = ipcom_in_checksum_finish(csum)); } IP_STATIC Ipnet_payload_icmp6_t * ipnet_payload_icmp6_new(Ip_u8 type, Ip_u8 code, Ip_u16 r0, Ip_u16 r1, const void *data, Ip_size_t datalen) { static Ipnet_payload_ops ops = { ipnet_payload_icmp6_dtor, ipnet_payload_icmp6_len, ipnet_payload_icmp6_csum, ipnet_payload_icmp6_serialize }; Ipnet_payload_icmp6_t *p; p = ipcom_calloc(1, sizeof(*p)); p->payload.type = IP_IPPROTO_ICMPV6; p->payload.ops = &ops; p->icmplen = 8 + datalen; p->icmp = ipcom_malloc(p->icmplen); p->icmp->icmp6_type = type; p->icmp->icmp6_code = code; p->icmp->ipnet_icmp6_data16[0] = r0; p->icmp->ipnet_icmp6_data16[1] = r1; ipcom_memcpy(p->icmp + 1, data, datalen); return p; } IP_STATIC Ipnet_payload_icmp6_t * ipnet_payload_icmp6_rt_adv_new(Ip_u16 rt_lifetime) { Ip_u32 d[2] = { 0, 0 }; return ipnet_payload_icmp6_new(ICMP6_T_RT_ADV, 0, 0, rt_lifetime, d, sizeof(d)); } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * ipnet_ip_or_ip6_match *=========================================================================== * Description: * Parameters: ip - pointer to IPv4 or IPv6 header * src - source IP address to match * dst - destination IP address to match * ipproto - transport protocol to match * Returns: pointer to the transport header or IP_NULL if no match. * */ IP_STATIC const void * ipnet_ip_or_ip6_match(const void *ip, const char *src, const char *dst, Ip_u8 ipproto) { const void *tr_hdr = IP_NULL; #ifdef IPCOM_USE_INET tr_hdr = ipnet_ip_match(ip, src, dst, ipproto); #endif #ifdef IPCOM_USE_INET6 if (tr_hdr == IP_NULL) tr_hdr = ipnet_ip6_match(ip, src, dst, ipproto); #endif return tr_hdr; } /* * Helper functions for TCP */ IP_STATIC const Ipnet_pkt_tcp * ipnet_tcp_match(const void *_tcp, Ip_in_port_t sport, Ip_in_port_t dport) { const Ipnet_pkt_tcp *tcp = _tcp; if (tcp != IP_NULL && ip_ntohs(tcp->sport) == sport && ip_ntohs(tcp->dport) == dport) return tcp; return IP_NULL; } #ifdef IPCOM_USE_INET IP_STATIC const Ipnet_pkt_tcp * ipnet_tcp_wait_for(int dev_sock, const char *saddr, const char *daddr, Ip_in_port_t sport, Ip_in_port_t dport, Ip_u32 seq, Ip_u32 ack, Ip_u16 must_flags, Ip_u16 must_not_flags) { static char rdata[100]; const Ipnet_pkt_tcp *tcp_p; do { if (ipcom_recv(dev_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); tcp_p = ipnet_tcp_match(ipnet_ip_or_ip6_match(rdata, saddr, daddr, IP_IPPROTO_TCP), sport, dport); if (tcp_p != IP_NULL && ack != 0 && ip_ntohl(tcp_p->ack_num) != ack) tcp_p = IP_NULL; if (tcp_p != IP_NULL && seq != 0 && ip_ntohl(tcp_p->seq_num) != seq) tcp_p = IP_NULL; if (tcp_p != IP_NULL && (tcp_p->offset_flags & must_flags) != must_flags) tcp_p = IP_NULL; if (tcp_p != IP_NULL && (tcp_p->offset_flags & must_not_flags)) tcp_p = IP_NULL; } while (IP_NULL == tcp_p); return tcp_p; } #endif /* IPCOM_USE_INET */ /* *=========================================================================== * ipnet_post_expect_nothing *=========================================================================== * Description: Verifies that nothing has been received on the socket. * Parameters: sock - a socket * Returns: * */ IP_STATIC void ipnet_post_expect_nothing(int sock) { int i; int bytes_ready_to_read; for (i = 0; i < 2; i++) if (ipcom_socketioctl(sock, IP_SIOCINQ, &bytes_ready_to_read) < 0 || bytes_ready_to_read != 0) IPNET_POST_FAILED(); } #ifdef IPCOM_USE_INET /* *=========================================================================== * ipnet_post_tcp4 *=========================================================================== * Description: * Parameters: session - * Returns: * */ IP_STATIC void ipnet_post_tcp4(Ipnet_post_session_t *session) { Ipnet_payload_ip_t *ip; Ipnet_payload_tcp_t *tcp; Ipnet_payload_data_t *data; const Ipnet_pkt_tcp *tcp_p; const char *raddr = inet_addrs[0].remote; const char *laddr = inet_addrs[0].local; Ip_in_port_t lport; Ip_in_port_t rport = 502; Ip_u32 peer_ack = 0; Ip_u32 peer_seq; Ip_size_t datalen = 10; struct Ip_timeval tv = { 0, 500000 }; struct Ip_timeval org_tv; Ip_socklen_t org_tv_len = sizeof(org_tv); char rdata[100]; Ip_fd_set wr_set; int err; Ip_socklen_t errlen = sizeof(err); int retries; int app_sock; /* * Traffic pattern from WIND00211288 */ if (ipcom_getsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &org_tv, &org_tv_len) < 0) IPNET_POST_FAILED(); if (ipcom_setsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &tv, sizeof(tv)) < 0) IPNET_POST_FAILED(); app_sock = ipnet_post_tcp_session_new(laddr, 0); if (ipcom_setsockopt(app_sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &tv, sizeof(tv)) < 0) IPNET_POST_FAILED(); lport = ipnet_post_get_local_port(app_sock); ipnet_post_connect_socket(app_sock, raddr, rport, IP_TRUE); tcp = ipnet_payload_tcp_new(rport, lport); ip = ipnet_payload_append(ipnet_payload_ip_new(raddr, laddr), tcp); /* Wait for SYN segment */ tcp_p = ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, 0, 0, TCP_F_SYN, TCP_F_ACK); peer_seq = ip_ntohl(tcp_p->seq_num); ipnet_payload_tcp_ack(tcp, ++peer_seq); ipnet_payload_tcp_seq(tcp, peer_ack++); ipnet_payload_tcp_flags(tcp, TCP_F_SYN | TCP_F_ACK); for (retries = 0; retries < 3 || (IPNET_POST_FAILED(),0); retries++) { ipnet_payload_send(ip, session->dev0_sock); IP_FD_ZERO(&wr_set); IP_FD_SET(app_sock, &wr_set); if (ipcom_socketselect(app_sock + 1, IP_NULL, &wr_set, IP_NULL, &tv) == 1) break; } if (ipcom_getsockopt(app_sock, IP_SOL_SOCKET, IP_SO_ERROR, &err, &errlen) < 0 || err != 0) IPNET_POST_FAILED(); ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, peer_seq, peer_ack, TCP_F_ACK, TCP_F_SYN); /* * 3-way handshake done */ /* * Send "request" */ data = ipnet_payload_data_new(datalen); tcp = ipnet_payload_append(tcp, data); ipnet_payload_ip_narrow(ip, 0, RANGE_END); ipnet_payload_tcp_seq(tcp, peer_ack); peer_ack += (Ip_u32) datalen; ipnet_payload_tcp_flags(tcp, TCP_F_PSH | TCP_F_ACK); ipnet_payload_send(ip, session->dev0_sock); /* * Recv "request", send "reponse" */ if (ipcom_recv(app_sock, rdata, datalen, IP_MSG_WAITALL) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); if (ipcom_send(app_sock, rdata, datalen, 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); /* * Check that the response was sent */ ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, peer_seq, peer_ack, TCP_F_ACK | TCP_F_PSH, 0); peer_seq += (Ip_u32) datalen; /* * Retransmit the "request" */ ipnet_payload_send(ip, session->dev0_sock); /* * Verify that a duplicate ACK is sent */ ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, peer_seq, peer_ack, TCP_F_ACK, TCP_F_PSH); /* * Send keep-alive, i.e send last byte of something already sent */ ipnet_payload_data_set_len(data, 1); ipnet_payload_ip_narrow(ip, 0, RANGE_END); ipnet_payload_tcp_seq(tcp, peer_ack - 1); ipnet_payload_send(ip, session->dev0_sock); /* * Verify that a duplicate ACK is sent */ ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, peer_seq, peer_ack, TCP_F_ACK, TCP_F_PSH); /* * Send keep-alive again, this should trigger a fast retransmit */ ipnet_payload_send(ip, session->dev0_sock); /* * Check for fast retransmit */ ipnet_tcp_wait_for(session->dev0_sock, laddr, raddr, lport, rport, peer_seq - (Ip_u32)datalen, peer_ack, TCP_F_ACK | TCP_F_PSH, 0); ipnet_payload_delete_all(ip); ipnet_post_app_sock_delete(app_sock); if (ipcom_setsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &org_tv, org_tv_len) < 0) IPNET_POST_FAILED(); } /* *=========================================================================== * ipnet_post_udp4_congest_netif *=========================================================================== * Description: Flood the network interface with data so it starts to * return EWOULDBLOCK. This verify operation of the fast-FIFO. * Parameters: session - a POST session * Returns: * */ IP_STATIC void ipnet_post_udp4_congest_netif(Ipnet_post_session_t *session) { const char *raddr = inet_addrs[0].remote; const char *laddr = inet_addrs[0].local; int csock; char data[1000]; int dev_rcv_buf_sz = 5000; int org_dev_rcv_buf_sz; Ip_socklen_t rcv_buf_len = sizeof(int); int i; int cnt = 0; int restart_cnt = -1; const int expected_cnt = 10; struct Ip_ifreq ifreq; ipcom_strcpy(ifreq.ifr_name, session->dev0_ifname); csock = ipnet_post_udp_session_new(laddr, 0); ipnet_post_connect_socket(csock, raddr, ipnet_post_get_local_port(csock), IP_FALSE); if (ipcom_getsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVBUF, &org_dev_rcv_buf_sz, &rcv_buf_len) < 0 || rcv_buf_len != sizeof(int)) IPNET_POST_FAILED(); if (ipcom_setsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVBUF, &dev_rcv_buf_sz, sizeof(org_dev_rcv_buf_sz)) < 0) IPNET_POST_FAILED(); ipnet_post_dev_flush(session->dev0_sock); for (i = 0; i <= expected_cnt; i++) { if (ipcom_send(csock, data, sizeof(data), 0) != sizeof(data)) IPNET_POST_FAILED(); if (i == 0) while (ipcom_recv(session->dev0_sock, data, sizeof(data), 0) != sizeof(data)) continue; } for (;;) { i = ipcom_recv(session->dev0_sock, data, sizeof(data), IP_MSG_DONTWAIT); if (i < 0) { if (ipcom_errno == IP_ERRNO_EWOULDBLOCK) { if (cnt == restart_cnt) break; /* * This will restart the interface. */ ifreq.ip_ifr_flags = IP_FLAG_FC_STACKCONTEXT; if (ipcom_socketioctl(session->dev0_sock, IP_SIOCSIFPRIVATE, &ifreq) < 0) IPNET_POST_FAILED(); restart_cnt = cnt; } else IPNET_POST_FAILED(); } else if (i == sizeof(data)) cnt++; } if (cnt < expected_cnt) IPNET_POST_FAILED(); if (ipcom_setsockopt(session->dev0_sock, IP_SOL_SOCKET, IP_SO_RCVBUF, &org_dev_rcv_buf_sz, sizeof org_dev_rcv_buf_sz) < 0) IPNET_POST_FAILED(); ipnet_post_app_sock_delete(csock); } /* *=========================================================================== * ipnet_post_udp4_fragment *=========================================================================== * Description: Fragmented IPv4 UDP datagrams. * Parameters: session - a POST session * Returns: * */ IP_STATIC void ipnet_post_udp4_fragment(Ipnet_post_session_t *session) { Ipnet_payload_ip_t *ip; Ipnet_payload_ip_t *ip2; const char *raddr = inet_addrs[0].remote; const char *laddr = inet_addrs[0].local; Ip_in_port_t rport = 0x1234; Ip_in_port_t lport; char rdata[100]; const Ip_size_t datalen = 40; int app_sock; app_sock = ipnet_post_udp_session_new(laddr, 0); lport = ipnet_post_get_local_port(app_sock); ipnet_post_connect_socket(app_sock, raddr, rport, IP_FALSE); ip = ipnet_payload_append(ipnet_payload_ip_new(raddr, laddr), ipnet_payload_append(ipnet_payload_udp_new(rport, lport), ipnet_payload_data_new(datalen))); ipnet_post_dev_flush(session->dev0_sock); /* Two non-overlaping fragments */ ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 8, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); /* Three non-overlaping fragments sent in reverse order */ ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 24, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 8, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); /* First fragment length is not a multiple of 8, WIND00236128 */ ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 0, 19); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 24, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); ipnet_post_expect_nothing(app_sock); /* * First and second IP datagram switches fragment head. Tests a * very implementation specific detail in IPNET. All partly * reassembled IP-datagrams are put into a list where the fragment * with the currently lowest offset is part of. The "next" pointer * in this list need to be updated when/if a fragment arrives that * has a lower offset. */ ip2 = ipnet_payload_append(ipnet_payload_ip_new(raddr, laddr), ipnet_payload_append(ipnet_payload_udp_new(rport, lport), ipnet_payload_data_new(datalen-4))); ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 8, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_change_id(ip2); ipnet_payload_ip_narrow(ip2, 24, RANGE_END); ipnet_payload_send(ip2, session->dev0_sock); ipnet_payload_ip_narrow(ip2, 8, 16); ipnet_payload_send(ip2, session->dev0_sock); /* Finish the first IP datagram */ ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); /* Finish the second IP datagram */ ipnet_payload_ip_narrow(ip2, 0, 8); ipnet_payload_send(ip2, session->dev0_sock); ipnet_payload_delete_all(ip2); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t) (datalen-4)) IPNET_POST_FAILED(); /* Generate "holes" that is later filled */ ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 32, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 0, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 24, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 40, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); /* * Overlaping fragments, pattern from WIND00207096 */ ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 0, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 24); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 24, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 0, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 8, 16); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 24, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 8, 24); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 8, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 24, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 32, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 16, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 32, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip_narrow(ip, 8, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); if (0) { unsigned i; for (i=0; i <= ipnet_conf_max_reassembly_list_len; i++) { ipnet_payload_ip_change_id(ip); ipnet_payload_ip_narrow(ip, 8, 8); ipnet_payload_send(ip, session->dev0_sock); } } ipnet_payload_delete_all(ip); ipnet_post_app_sock_delete(app_sock); } IP_STATIC void ipnet_post_ip_ping(const char *daddr, const char *saddr, int dev_sock) { Ipnet_payload_ip_t *ip; char data[100]; ip = ipnet_payload_append(ipnet_payload_ip_new(saddr, daddr), ipnet_payload_icmp_new(ICMP_T_ECHO_REQ, 0, 1, 2, IP_NULL, 0)); ipnet_payload_send(ip, dev_sock); ipnet_payload_delete_all(ip); do { if (ipcom_recv(dev_sock, data, sizeof(data), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp_match(ipnet_ip_match(data, daddr, saddr, IP_IPPROTO_ICMP), ICMP_T_ECHO_RES, 0, IP_NULL, 0)); } IP_STATIC void ipnet_post_ip4_ipopt(Ipnet_post_session_t *session) { Ipnet_payload_ip_t *ip; const char *src = inet_addrs[0].remote; const char *dst = inet_addrs[0].local; Ip_u8 opts[12]; const char *echo_data = "ECHO with IP-opts"; int bytes_ready_to_read; char rdata[100]; Ip_u8 problem_ptr; /* * The IP payload it totally irrelevant for this test */ ip = ipnet_payload_append(ipnet_payload_ip_new(src, dst), ipnet_payload_icmp_new(ICMP_T_ECHO_REQ, 0, 1, 2, echo_data, ipcom_strlen(echo_data))); ipnet_post_dev_flush(session->dev0_sock); /* * Values from defect WIND00242330. First fragment has a valid * timestamp option, but the second option has not enough space * between "offset" and the end of the option to fit a * timestamp. No ICMP parameter problem must be sent since the * error is not in the first fragment. */ ipcom_memset(opts, 0, sizeof(opts)); opts[0] = 0x44; opts[1] = 0x0c; opts[2] = 0x09; opts[4] = 0x11; opts[5] = 0x22; opts[6] = 0x33; opts[7] = 0x44; ipnet_payload_ip_set_opts(ip, opts, 12); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); /* Option length decreased to 10 in this fragment */ opts[1] = 0x0a; ipnet_payload_ip_set_opts(ip, opts, 12); ipnet_payload_ip_narrow(ip, 8, (Ip_size_t)-1); ipnet_payload_send(ip, session->dev0_sock); /* * This is just to know that the ECHO request has been processed * without adding any for of "sleep" calls */ ipnet_post_ip_ping(inet_addrs[1].local, inet_addrs[1].remote, session->dev1_sock); /* * Verify that we do NOT get an ICMP parameter problem. */ while (ipcom_socketioctl(session->dev0_sock, IP_SIOCINQ, &bytes_ready_to_read) >= 0 && bytes_ready_to_read > 0) { if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), IP_MSG_DONTWAIT) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, IP_NULL, 0)) IPNET_POST_FAILED(); } /* SUCCESS */ /* * Values from defect WIND00242328. Not enough space between * "offset" and end of option to insert a timestamp. */ ipcom_memset(opts, 0, sizeof(opts)); opts[0] = 0x44; opts[1] = 0x0a; opts[2] = 0x09; opts[4] = 0x03; opts[5] = 0x4b; opts[6] = 0x3e; opts[7] = 0xde; ipnet_payload_ip_set_opts(ip, opts, 12); ipnet_payload_ip_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); /* Bad field is "offset" */ problem_ptr = 22; for (;;) { /* * Wait for ICMP parameter problem message. */ if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, &problem_ptr, 1)) break; } /* * Values from defect WIND00236627. Some data, but not enough to * insert a TS entry, is available on the egress side. */ ipcom_memset(opts, 0, sizeof(opts)); opts[0] = 0x44; opts[1] = 0x09; opts[2] = 0x05; ipnet_payload_ip_set_opts(ip, opts, sizeof(opts)); ipnet_payload_ip_narrow(ip, 0, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); /* * This is just to know that the ECHO request has been processed * without adding any for of "sleep" calls */ ipnet_post_ip_ping(inet_addrs[1].local, inet_addrs[1].remote, session->dev1_sock); /* * Verify that we do NOT get an ICMP echo response. */ while (ipcom_socketioctl(session->dev0_sock, IP_SIOCINQ, &bytes_ready_to_read) >= 0 && bytes_ready_to_read > 0) { if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), IP_MSG_DONTWAIT) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_ECHO_RES, 0, IP_NULL, 0)) IPNET_POST_FAILED(); } /* SUCCESS */ /* * Some data, but not enough to insert a TS entry, is avaiable * when the ingress tries to add an TS */ opts[1] = 0x07; ipnet_payload_ip_set_opts(ip, opts, sizeof(opts)); ipnet_payload_send(ip, session->dev0_sock); for (;;) { /* * Wait for ICMP parameter problem message. */ if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, IP_NULL, 0)) break; } /* SUCCESS */ /* * WIND00239953, must not process IP-datagrams with invalid * IP-options */ ipcom_memset(opts, 0, sizeof(opts)); opts[0] = 0x01; opts[1] = 0x01; opts[2] = 0x94; opts[3] = 0x01; ipnet_payload_ip_set_opts(ip, opts, sizeof(opts)); ipnet_payload_send(ip, session->dev0_sock); /* * Verify that we got a parameter problem pointing to opts[3] * offset == 23, 20 bytes IP header and 3 bytes into the options. */ problem_ptr = 23; for (;;) { /* * Wait for ICMP parameter problem message. */ if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, &problem_ptr, 1)) break; } /* SUCCESS */ /* * Verify that the stack can handle IP-options that claims too big * length. The returned problem pointer should point to the length * field. */ opts[0] = 0x94; opts[1] = 0x10; ipnet_payload_ip_set_opts(ip, opts, 4); ipnet_payload_send(ip, session->dev0_sock); problem_ptr = 21; for (;;) { /* * Wait for ICMP parameter problem message. */ if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, &problem_ptr, 1)) break; } /* SUCCESS */ /* * Verify that the stack can handle IP-options that has a header * that isn't complete (too short). Parameter problem must point * to the last valid byte in the options. */ opts[0] = 0x01; opts[1] = 0x01; opts[2] = 0x01; opts[3] = 0x94; ipnet_payload_ip_set_opts(ip, opts, 4); ipnet_payload_send(ip, session->dev0_sock); problem_ptr = 23; for (;;) { /* * Wait for ICMP parameter problem message. */ if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); if (ipnet_icmp_match(ipnet_ip_match(rdata, dst, src, IP_IPPROTO_ICMP), ICMP_T_PARM_PROB, 0, &problem_ptr, 1)) break; } /* SUCCESS */ ipnet_payload_delete_all(ip); /* * Verify fix for defect WIND00356370, record route option with * one address but length and offset to 0 (both invalid).. */ /* * Must use a non-local IP-address */ ip = ipnet_payload_append(ipnet_payload_ip_new(src, "192.168.1.3"), ipnet_payload_icmp_new(ICMP_T_ECHO_REQ, 0, 1, 2, echo_data, ipcom_strlen(echo_data))); opts[0] = 0x07; opts[1] = 0x00; opts[2] = 0x00; opts[3] = 0xa0; opts[4] = 0x01; opts[5] = 0x02; opts[6] = 0x03; opts[6] = 0x00; ipnet_payload_ip_set_opts(ip, opts, 8); ipnet_payload_send(ip, session->dev0_sock); /* SUCCESS as failure means system deadlocks and never reaches this line */ ipnet_payload_delete_all(ip); } /* *=========================================================================== * ipnet_post_inaddr_format *=========================================================================== * Description: Verifies that invalid IPv4 address formats is detected. * Parameters: * Returns: * */ IP_STATIC void ipnet_post_inaddr_format() { const char *addrs[] = { "10", /* Too few parts */ " 1.2.3.4", /* Invalid whitespace at start */ "1.2.3.4 ", /* Invalid whitespace at end */ "1 .2.3.4", /* Invalid whitespace after hexdigit */ "1. 2.3.4", /* Invalid whitespace before hexdigit */ "321.2.3.4", /* Too big hexdigit */ "1.+2.3.4", /* Invalid prefix */ "1.-2.3.4", /* Invalid prefix */ "1.0x2.3.4", /* Invalid prefix */ "1.0X2.3.4", /* Invalid prefix */ IP_NULL }; int i; struct Ip_in_addr res; for (i = 0; addrs[i] != IP_NULL; i++) if (ipcom_inet_pton(IP_AF_INET, addrs[i], &res)) IPNET_POST_FAILED(); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 /* *=========================================================================== * ipnet_post_ip6_ping *=========================================================================== * Description: ICMPv6 echo request/response tests. * Parameters: daddr - unicast address on the sockdev interface * saddr - unicast address that can be reached from the sockdev * interface * dev_sock - handle to the socket device * Returns: * */ IP_STATIC void ipnet_post_ip6_ping(const char *daddr, const char *saddr, int dev_sock) { Ipnet_payload_ip6_t *ip; char data[100]; ip = ipnet_payload_append(ipnet_payload_ip6_new(saddr, daddr, 64), ipnet_payload_icmp6_new(ICMP6_T_ECHO_REQ, 0, 0, 0, IP_NULL, 0)); ipnet_payload_send(ip, dev_sock); ipnet_payload_delete_all(ip); do { if (ipcom_recv(dev_sock, data, sizeof(data), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp6_match(ipnet_ip6_match(data, daddr, saddr, IP_IPPROTO_ICMPV6), ICMP6_T_ECHO_REP, 0)); /* * Send ECHO request that contains a hop-by-hop extension that * isn't the first extension. This must generate a ICMPv6 * parameter problem response, code==1. */ ip = ipnet_payload_append(ipnet_payload_ip6_new(saddr, daddr, 64), ipnet_payload_append(ipnet_payload_ip6ext_new(IP_IPPROTO_DSTOPTS), ipnet_payload_append(ipnet_payload_ip6ext_new(IP_IPPROTO_HOPOPTS), ipnet_payload_icmp6_new(ICMP6_T_ECHO_REQ, 0, 0, 0, IP_NULL, 0)))); ipnet_payload_send(ip, dev_sock); ipnet_payload_delete_all(ip); do { if (ipcom_recv(dev_sock, data, sizeof(data), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp6_match(ipnet_ip6_match(data, daddr, saddr, IP_IPPROTO_ICMPV6), ICMP6_T_PARAM_PROB, 1)); /* * Parameter problem, code==1, must also be sent if the upper layer is NONE. */ ip = ipnet_payload_append(ipnet_payload_ip6_new(saddr, daddr, 64), ipnet_payload_append(ipnet_payload_ip6ext_new(IP_IPPROTO_FRAGMENT), ipnet_payload_ip6ext_new(IP_IPPROTO_HOPOPTS))); ipnet_payload_send(ip, dev_sock); ipnet_payload_delete_all(ip); do { if (ipcom_recv(dev_sock, data, sizeof(data), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp6_match(ipnet_ip6_match(data, daddr, saddr, IP_IPPROTO_ICMPV6), ICMP6_T_PARAM_PROB, 1)); } /* *=========================================================================== * ipnet_post_rt6 *=========================================================================== * Description: IPv6 router advertisement test. * Parameters: session - a POST session. * Returns: * */ IP_STATIC void ipnet_post_rt6(Ipnet_post_session_t *session) { Ipnet_payload_ip6_t *ip; const char *rt0_addr = "fe80::1000"; const char *rt1_addr = "fe80::2000"; const char *laddr0 = inet6_addrs[0].local; const char *laddr1 = inet6_addrs[1].local; struct Ip_sockaddr_in6 peer; Ipnet_pkt_icmp6 echo; int app_sock; app_sock = ipnet_post_icmp6_sock_new("::"); if (ipcom_shutdown(app_sock, IP_SHUT_RD) < 0) IPNET_POST_FAILED(); /* * Send rt_adv on first link, lifetime == 1000 */ ip = ipnet_payload_append(ipnet_payload_ip6_new(rt0_addr, "ff02::1", 255), ipnet_payload_icmp6_rt_adv_new(1000)); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_delete_all(ip); /* * Send rt_adv on second link, lifetime == 1000 */ ip = ipnet_payload_append(ipnet_payload_ip6_new(rt1_addr, "ff02::1", 255), ipnet_payload_icmp6_rt_adv_new(1000)); ipnet_payload_send(ip, session->dev1_sock); ipnet_payload_delete_all(ip); /* * Send echo request, both rt_adv has been processed when the echo * response is received. */ ipnet_post_ip6_ping(laddr0, rt0_addr, session->dev0_sock); ipnet_post_ip6_ping(laddr1, rt1_addr, session->dev1_sock); /* * Send an ICMPv6 message to 16 different peers that can be * reached through any of the available routers. * * WIND00241507 is present if the source address of the outgoing * packet is selected from one interface and it is sent on the * other. */ ipcom_memset(&echo, 0, sizeof(echo)); echo.icmp6_type = ICMP6_T_ECHO_REQ; ipcom_memset(&peer, 0, sizeof(peer)); peer.sin6_family = IP_AF_INET6; IPCOM_SA_LEN_SET(&peer, sizeof(peer)); ipcom_inet_pton(IP_AF_INET6, "fcff::00ac", &peer.sin6_addr); do { char data[100]; char str[IP_INET6_ADDRSTRLEN]; const char *expected_saddr; /* * Send ECHO request through default gateway */ if (ipcom_sendto(app_sock, &echo, sizeof(echo), 0, (void *)&peer, sizeof(peer)) < 0) IPNET_POST_FAILED(); /* * Loop until we find the ECHO request on one of the socket * devices. */ do { int dev_sock; Ip_fd_set rd_set; struct Ip_timeval tv = { 10, 0 }; IP_FD_ZERO(&rd_set); IP_FD_SET(session->dev0_sock, &rd_set); IP_FD_SET(session->dev1_sock, &rd_set); if (ipcom_socketselect(1 + IP_MAX(session->dev1_sock, session->dev1_sock), &rd_set, IP_NULL, IP_NULL, &tv) <= 0) IPNET_POST_FAILED(); if (IP_FD_ISSET(session->dev0_sock, &rd_set)) { dev_sock = session->dev0_sock; expected_saddr = laddr0; } else { dev_sock = session->dev1_sock; expected_saddr = laddr1; } if (ipcom_recv(dev_sock, data, sizeof(data), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp6_match(ipnet_ip6_match(data, expected_saddr, ipcom_inet_ntop(IP_AF_INET6, &peer.sin6_addr, str, sizeof(str)), IP_IPPROTO_ICMPV6), ICMP6_T_ECHO_REQ, 0)); } while (++peer.sin6_addr.in6.addr8[14] < 0x10); /* * Send rt_adv on first link, lifetime == 0 to remove the default gateway */ ip = ipnet_payload_append(ipnet_payload_ip6_new(rt0_addr, "ff02::1", 255), ipnet_payload_icmp6_rt_adv_new(0)); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_delete_all(ip); /* * Send rt_adv on second link, lifetime == 0 to remove the default gateway */ ip = ipnet_payload_append(ipnet_payload_ip6_new(rt1_addr, "ff02::1", 255), ipnet_payload_icmp6_rt_adv_new(0)); ipnet_payload_send(ip, session->dev1_sock); ipnet_payload_delete_all(ip); ipnet_post_app_sock_delete(app_sock); } /* *=========================================================================== * ipnet_post_udp6_fragment *=========================================================================== * Description: Fragmented IPv6 UDP datagrams. * Parameters: session - a POST session * Returns: * */ IP_STATIC void ipnet_post_udp6_fragment(Ipnet_post_session_t *session) { Ipnet_payload_ip6_t *ip; const char *raddr = inet6_addrs[0].remote; const char *laddr = inet6_addrs[0].local; Ip_in_port_t rport = 0x6666; Ip_in_port_t lport; char rdata[100]; Ip_size_t datalen = 40; Ip_size_t i; int app_sock; app_sock = ipnet_post_udp_session_new(laddr, 0); lport = ipnet_post_get_local_port(app_sock); ipnet_post_connect_socket(app_sock, raddr, rport, IP_FALSE); ip = ipnet_payload_append(ipnet_payload_ip6_new(raddr, laddr, 64), ipnet_payload_append(ipnet_payload_udp_new(rport, lport), ipnet_payload_data_new(datalen))); ipnet_post_dev_flush(session->dev0_sock); /* Two non-overlaping fragments */ ipnet_payload_ip6_narrow(ip, 0, 8); ipnet_payload_send(ip, session->dev0_sock); ipnet_payload_ip6_narrow(ip, 8, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), 0) != (Ip_ssize_t)datalen) IPNET_POST_FAILED(); ipnet_payload_delete_all(ip); /* * Verify fix for defect WIND00194935 * * Must send a ICMPv6 parameter problem if the IP-datagram is * reassembled to > 65535 bytes. */ datalen = 65536; ip = ipnet_payload_append(ipnet_payload_ip6_new(raddr, laddr, 64), ipnet_payload_append(ipnet_payload_udp_new(rport, lport), ipnet_payload_data_new(datalen))); for (i = 0; i < datalen / 1400; i++) { ipnet_payload_ip6_narrow(ip, i*1400, 1400); ipnet_payload_send(ip, session->dev0_sock); } ipnet_payload_ip6_narrow(ip, i*1400, RANGE_END); ipnet_payload_send(ip, session->dev0_sock); if (ipcom_recv(app_sock, rdata, sizeof(rdata), IP_MSG_DONTWAIT) != -1) /* * Nothing should be delivered to the application socket */ IPNET_POST_FAILED(); do { if (ipcom_recv(session->dev0_sock, rdata, sizeof(rdata), 0) < 0) IPNET_POST_FAILED(); } while (!ipnet_icmp6_match(ipnet_ip6_match(rdata, laddr, raddr, IP_IPPROTO_ICMPV6), ICMP6_T_PARAM_PROB, 0 /* code: error in header */)); ipnet_payload_delete_all(ip); ipnet_post_app_sock_delete(app_sock); } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * ipnet_post_pkt *=========================================================================== * Description: Verify packet operations, also verifies fix for WIND00287060. * Parameters: * Returns: * */ IP_STATIC void ipnet_post_pkt() { Ipcom_pkt *pkt; pkt = ipcom_pkt_malloc(16000, 0); if (IP_NULL == pkt) IPNET_POST_FAILED(); if (1 != ipcom_pkt_get_partcnt(pkt)) IPNET_POST_FAILED(); ipcom_pkt_reserve_data(pkt, 200); if (0 != ipcom_pkt_get_length(pkt)) IPNET_POST_FAILED(); ipcom_pkt_push_back(pkt, 130); if (130 != ipcom_pkt_get_length(pkt)) IPNET_POST_FAILED(); ipcom_pkt_pop_back(pkt, 20); if (110 != ipcom_pkt_get_length(pkt)) IPNET_POST_FAILED(); ipcom_pkt_pop_front(pkt, 10); if (100 != ipcom_pkt_get_length(pkt)) IPNET_POST_FAILED(); pkt->next_part = ipnet_pkt_clone(pkt, IP_FALSE); if (pkt->next_part == IP_NULL) IPNET_POST_FAILED(); pkt->next_part->next_part = ipnet_pkt_clone(pkt->next_part, IP_FALSE); if (pkt->next_part->next_part == IP_NULL) IPNET_POST_FAILED(); if (3 != ipcom_pkt_get_partcnt(pkt)) IPNET_POST_FAILED(); if (300 != ipcom_pkt_get_length(pkt)) IPNET_POST_FAILED(); ipcom_pkt_free(pkt); } /* *=========================================================================== * ipnet_post_if *=========================================================================== * Description: Verifies operation of if_nametoindex(), if_indextoname() * and if_nameindex() * Parameters: * Returns: * */ IP_STATIC void ipnet_post_if() { struct Ip_if_nameindex *ni; int i; Ip_u32 valid_ifindex; Ip_u32 invalid_ifindex; char ifname[IP_IFNAMSIZ]; ni = ipcom_if_nameindex(); if (ni == IP_NULL || ni[0].if_index == 0) IPNET_POST_FAILED(); valid_ifindex = ni[0].if_index; for (i = 0; ni[i].if_index > 0; i++) if (invalid_ifindex <= ni[i].if_index) invalid_ifindex = ni[i].if_index + 1; ipcom_if_freenameindex(ni); if (ipcom_if_indextoname(0, ifname) != IP_NULL) IPNET_POST_FAILED(); if (ipcom_if_indextoname(invalid_ifindex, ifname) != IP_NULL) IPNET_POST_FAILED(); if (ipcom_if_indextoname(valid_ifindex, ifname) == IP_NULL) IPNET_POST_FAILED(); if (ipcom_if_nametoindex(ifname) != valid_ifindex) IPNET_POST_FAILED(); ipcom_strcpy(ifname, "notPresent"); if (ipcom_if_nametoindex(ifname) != 0) IPNET_POST_FAILED(); } #ifdef IPCOM_USE_INET6 /* *=========================================================================== * ipnet_post_in6addr_format *=========================================================================== * Description: Verifies that invalid IPv6 address formats is detected. * Parameters: * Returns: * */ IP_STATIC void ipnet_post_in6addr_format() { int i; struct Ip_in6_addr res; const char *invalid_addrs[] = { " 1:2::3:4", /* Invalid whitespace at start */ "1:2::3:4 ", /* Invalid whitespace at end */ "1 :2::3:4", /* Invalid whitespace after hexdigit */ "1: 2::3:4", /* Invalid whitespace before hexdigit */ "12345:2::3:4", /* Too big hexdigit */ "1:+2::3:4", /* Invalid prefix */ "1:-2::3:4", /* Invalid prefix */ "1:0x2::3:4", /* Invalid prefix */ "1:0X2::3:4", /* Invalid prefix */ "::1.2.3", /* Not a dotted quad at the end */ "B123-12", ":::1.1.1.1", "2002:::1.1.1.1", "2002::1:", /* :: is just a replacement for a normal : */ "2001:db8::1:1:1:1:1:1", /* RFC 5952, "::" must represent more than one 16-bit 0 field */ "::1:2:3:4:5:6:7:8", /* Same as above */ "1:2:3:4:5:6:7:8::", /* Same as above */ "1:2:3:4:5:6:7:1.2.3.4", IP_NULL }; const char *valid_addrs[] = { /* * :: represents a single 16-bit zero field, these address is * really according to RFC 5952 but too many programs break * unless we accept them */ "2001:db8::1:1:1:1:1", /* RFC 5952, "::" must represent more than one 16-bit 0 field */ "::1:2:3:4:5:6:7", /* Same as above */ "1:2:3:4:5:6:7::", /* Same as above */ #ifdef IPCOM_USE_INET "1:2:3:4:5:6:1.2.3.4", "0:0:0:0:0:ffff:192.0.100.1", "0:0:0:0:0:0:192.0.200.1", "0123::1234:192.0.100.1", "::1234:192.0.100.1", #endif /* IPCOM_USE_INET */ IP_NULL }; for (i = 0; invalid_addrs[i] != IP_NULL; i++) if (ipcom_inet_pton(IP_AF_INET6, invalid_addrs[i], &res)) IPNET_POST_FAILED(); for (i = 0; valid_addrs[i] != IP_NULL; i++) if (ipcom_inet_pton(IP_AF_INET6, valid_addrs[i], &res) != 1) IPNET_POST_FAILED(); } #endif /* IPCOM_USE_INET6 */ /* *=========================================================================== * ipnet_post_do *=========================================================================== * Description: Runs all test. * Parameters: session - session containing two sockdev that has been * initialized. * Returns: * */ IP_STATIC void ipnet_post_do(Ipnet_post_session_t *session) { ipnet_post_pkt(); ipnet_post_if(); #ifdef IPCOM_USE_INET ipnet_post_tcp4(session); ipnet_post_udp4_fragment(session); ipnet_post_ip4_ipopt(session); ipnet_post_udp4_congest_netif(session); ipnet_post_inaddr_format(); #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 ipnet_post_rt6(session); ipnet_post_udp6_fragment(session); ipnet_post_in6addr_format(); #endif } /* * Process/task that runs the POST. */ IP_STATIC IPCOM_PROCESS(ipnet_post) { Ipnet_post_session_t session = { 0, }; ipcom_proc_init(); ipcom_proc_vr_set(vr); ipnet_post_do_vr_op(IP_TRUE); session.dev0_sock = ipnet_post_dev_sock_open(0, session.dev0_ifname); session.dev1_sock = ipnet_post_dev_sock_open(1, session.dev1_ifname); ipnet_post_do(&session); ipcom_socketclose(session.dev0_sock); ipcom_socketclose(session.dev1_sock); ipnet_post_do_vr_op(IP_FALSE); IPCOM_LOG0(INFO, "POST successfull"); ipcom_proc_exit(); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL void ipnet_post_run(void) { Ipcom_proc_attr attr; ipcom_proc_attr_init(&attr); attr.priority = IPCOM_PRIORITY_DEFAULT + 1; attr.stacksize = IPCOM_PROC_STACK_LARGE; if (ipcom_proc_acreate("tNetPost", ipnet_post, &attr, IP_NULL) != IPCOM_SUCCESS) IPNET_POST_FAILED(); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ #else int ipnet_post_empty_file; #endif /* **************************************************************************** * END OF FILE **************************************************************************** */