Files
m241Neelima/components/ip_net2-6.9/ipnet2/src/ipnet_post.c
T
2026-07-30 11:19:44 +05:30

3289 lines
93 KiB
C

/*
******************************************************************************
* 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 <kenneth.jonsson@windriver.com>
******************************************************************************
*/
/*
****************************************************************************
* 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 <ipcom_cstyle.h>
#include <ipcom_errno.h>
#include <ipcom_inet.h>
#include <ipcom_os.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#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
****************************************************************************
*/