3289 lines
93 KiB
C
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
|
|
****************************************************************************
|
|
*/
|
|
|