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

2913 lines
91 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_sockopt.c,v $ $Revision: 1.229.10.16 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_sockopt.c,v $
* $Author: xxiong $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* ipcom_getsockopt() and ipcom_setsockopt() functions.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#define IPCOM_SKIP_NATIVE_SOCK_API
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_syslog.h>
#include <ipcom_sysvar.h>
#include "ipnet_h.h"
#include "ipnet_usr_sock.h"
#ifdef IPCOM_USE_INET6
#include "ipnet_icmp6.h"
#endif
#ifdef IPTCP
#include <iptcp.h>
#endif
#ifdef IPFIREWALL
#include <ipfirewall.h>
#include <ipfirewall_h.h>
#endif
#ifdef IPCOM_WV_INSTRUMENTATION
#include <ipcom_windview.h>
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC int ipnet_getsockopt_sol(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen);
IP_STATIC int ipnet_setsockopt_sol(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen);
#ifdef IPCOM_USE_INET
IP_STATIC int ipnet_getsockopt_ip4(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen);
IP_STATIC int ipnet_setsockopt_ip4(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen);
#endif
#ifdef IPCOM_USE_INET6
IP_STATIC int ipnet_getsockopt_ip6(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen);
#endif
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sockopt_enter_exit_promisc
*===========================================================================
* Description: Enters/exits promiscuous mode using the specified socket
* as placeholder. Closing the socket will also leave trigger
* one leave for each unmatched join.
* Parameters: sock - A socket.
* netif - The network interface to join promisc mode.
* enter_promisc - IP_TRUE if promiscuous mode should be entered
* IP_FALSE to exit promiscuous mode.
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_sockopt_enter_exit_promisc(Ipnet_socket *sock, Ipnet_netif *netif, Ip_bool enter_promisc)
{
if (enter_promisc)
sock->promisc_count++;
else
{
if (sock->promisc_count == 0)
return -IP_ERRNO_EADDRNOTAVAIL;
sock->promisc_count--;
}
IPCOM_LOG2(INFO, "%s %s promiscuous mode",
netif->ipcom.name,
enter_promisc ? "enter" : "exit");
return ipnet_if_drv_ioctl(netif, IP_SIOCXPROMISC, &enter_promisc);
}
/*
*===========================================================================
* ipnet_receive_cb_init
*===========================================================================
* Description: Calls the receive callback for each packet in the receive
* buffer.
* Parameters: sock - The socket to which a receive callback was installed.
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_receive_cb_init(Ipnet_socket *sock)
{
Ipcom_pkt *pkt;
Ipcom_socket_eventcb event_cb = sock->ipcom.event_cb;
ip_assert(event_cb != IP_NULL);
for (pkt = sock->rcv_head; pkt != IP_NULL; pkt = pkt->next)
event_cb(&sock->ipcom, pkt, IP_SOEVENT_CB_INPKT);
return 0;
}
/*
*===========================================================================
* ipnet_getsockopt_sol
*===========================================================================
* Description: Socket option where level was set to IP_SOL_SOCKET.
* Parameters:
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_getsockopt_sol(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
int val = 0;
switch (optname)
{
case IP_UDP_X_CHKSUM:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);
break;
#ifdef IPCOM_USE_INET
case IP_SO_BROADCAST:
if (sock->ip4 == IP_NULL)
return -IP_ERRNO_EINVAL;
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_ALLOW_BROADCAST);
break;
#endif /* IPCOM_USE_INET */
case IP_SO_X_BYPASS_FLOW:
#ifdef IPCOM_USE_INET
if (sock->ip4 != IP_NULL)
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_BYPASS_FLOW);
else
#endif
#ifdef IPCOM_USE_INET6
if (sock->ip6 != IP_NULL)
val = (int) IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_BYPASS_FLOW);
else
#endif
return -IP_ERRNO_EINVAL;
break;
#ifdef IPTCP
case IP_SO_LINGER:
if (*optlen >= sizeof(struct Ip_linger))
{
struct Ip_linger *l = optval;
if(sock->tcb == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
*optlen = sizeof(struct Ip_linger);
l->l_onoff = (sock->tcb->sec_to_linger != IPCOM_ADDR_INFINITE);
l->l_linger = sock->tcb->sec_to_linger;
return 0;
}
return -IP_ERRNO_EINVAL;
#endif /* IPTCP */
#ifdef IPNET_USE_RFC2890
case IP_SO_X_GRE_KEY:
if (*optlen >= sizeof(struct Ip_gre_key))
{
struct Ip_gre_key *gk = optval;
*optlen = sizeof(struct Ip_gre_key);
gk->gk_onoff = IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_GRE_KEY);
gk->gk_key = sock->gre_key;
return 0;
}
return -IP_ERRNO_EINVAL;
#endif /* IPNET_USE_RFC2890 */
case IP_SO_REUSEADDR:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEADDR);
break;
case IP_SO_REUSEPORT:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_REUSEPORT);
break;
case IP_SO_DONTROUTE:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_DONTROUTE);
break;
case IP_SO_ERROR:
val = sock->ipcom.so_errno;
sock->ipcom.so_errno = 0;
break;
case IP_SO_SNDBUF:
val = IP_MAX(sock->send_max_bytes / IPNET_SOCKBUF_MULTIPLIER, IPNET_MIN_SNDBUF_SIZE);
break;
case IP_SO_RCVBUF:
val = IP_MAX(sock->recv_max_bytes / IPNET_SOCKBUF_MULTIPLIER, IPNET_MIN_RCVBUF_SIZE);
break;
case IP_SO_RCVTIMEO:
if (*optlen >= sizeof(struct Ip_timeval))
{
*optlen = sizeof(struct Ip_timeval);
if (sock->rcv_timeout == IP_NULL)
ipcom_memset(optval, 0, *optlen);
else
ipcom_memcpy(optval, sock->rcv_timeout, *optlen);
return 0;
}
return -IP_ERRNO_EINVAL;
#ifdef IPNET_USE_SO_CONNTIMEO
case IP_SO_CONNTIMEO:
if (*optlen >= sizeof(struct Ip_timeval))
{
if (sock->conn_timeout != IP_NULL)
{
*optlen = sizeof(struct Ip_timeval);
ipcom_memcpy(optval, sock->conn_timeout, *optlen);
return 0;
}
}
return -IP_ERRNO_EINVAL;
#endif
case IP_SO_TYPE:
val = sock->ipcom.type;
break;
case IP_SO_KEEPALIVE:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_KEEPALIVE);
break;
case IP_SO_X_PROBEDELAY:
val = sock->keep_alive_probe_delay;
break;
case IP_SO_BINDTODEVICE:
if (*optlen >= IP_IFNAMSIZ)
{
Ipnet_netif *netif;
*optlen = IP_IFNAMSIZ;
ipcom_memset(optval, 0, IP_IFNAMSIZ);
netif = ipnet_if_indextonetif(sock->vr_index, sock->bind_to_ifindex);
if (netif != IP_NULL)
ipcom_strncpy(optval, netif->ipcom.name, IP_IFNAMSIZ);
return 0;
}
return -IP_ERRNO_EINVAL;
case IP_SO_OOBINLINE:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_OOBINLINE);
break;
#ifdef IPNET_USE_PER_SOCKET_VLAN_TAG
case IP_SO_VLAN:
if (*optlen >= sizeof(struct Ip_sovlan))
{
struct Ip_sovlan *sovlan = optval;
sovlan->vlan_onoff = (sock->vlan_tag != 0);
sovlan->priority_tagged = !!(sock->vlan_tag & 0x1000);
sovlan->vid = (unsigned short) (sock->vlan_tag & 0x0fff);
sovlan->upriority = (unsigned short) (sock->vlan_tag >> 13);
return 0;
}
return -IP_ERRNO_EINVAL;
#endif /* IPNET_USE_PER_SOCKET_VLAN_TAG */
case IP_SO_X_EVENTCB_RO:
if (*optlen >= sizeof(Ipcom_socket_eventcb))
{
*optlen = sizeof(Ipcom_socket_eventcb);
*(Ipcom_socket_eventcb *) optval = sock->ipcom.event_cb;
return 0;
}
return -IP_ERRNO_EINVAL;
case IP_SO_X_VR:
val = sock->vr_index;
break;
case IP_SO_X_COOKIE:
if (*optlen < sizeof(void*))
goto errout;
*(void**) optval = sock->ipcom.cookie;
*optlen = sizeof(void*);
return 0;
case IP_SO_X_DRAINABLE:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_DRAINABLE);
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
if (*optlen >= sizeof(int))
{
*(int*) optval = val;
*optlen = sizeof(int);
return 0;
}
errout:
return -IP_ERRNO_EINVAL;
}
/*
*===========================================================================
* ipnet_setsockopt_sol
*===========================================================================
* Description: Socket option where level was set to IP_SOL_SOCKET.
* Parameters:
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_setsockopt_sol(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
int val;
Ip_socklen_t this_optlen;
Ip_u32 flag = 0;
/*
* Verify optlen size
*/
switch (optname)
{
case IP_SO_RCVTIMEO:
#ifdef IPNET_USE_SO_CONNTIMEO
case IP_SO_CONNTIMEO:
#endif
this_optlen = sizeof(struct Ip_timeval);
break;
case IP_SO_BINDTODEVICE:
this_optlen = IP_IFNAMSIZ;
break;
#ifdef IPTCP
case IP_SO_LINGER:
this_optlen = sizeof(struct Ip_linger);
break;
#endif
#ifdef IPNET_USE_RFC2890
case IP_SO_X_GRE_KEY:
this_optlen = sizeof(struct Ip_gre_key);
break;
#endif /* IPNET_USE_RFC2890 */
case IP_SO_X_COOKIE:
this_optlen = sizeof(void*);
break;
case IP_SO_X_EVENTCB_RO:
this_optlen = sizeof(Ipcom_socket_eventcb);
break;
#ifdef IPNET_USE_PER_SOCKET_VLAN_TAG
case IP_SO_VLAN:
this_optlen = sizeof(struct Ip_sovlan);
break;
#endif
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
#ifdef IPNET_SO_WAKEUP_HOOK
case IP_SO_X_WINDNET_SO:
this_optlen = sizeof(void*);
break;
#endif
#endif
default:
this_optlen = sizeof(int);
break;
}
if (optlen != this_optlen)
return -IP_ERRNO_EINVAL;
val = *(int*) optval;
/*
* Length is OK, apply the option to the socket.
*/
switch (optname)
{
#ifdef IPCOM_USE_INET
case IP_SO_BROADCAST:
if (sock->ip4 == IP_NULL)
return -IP_ERRNO_EPERM;
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_ALLOW_BROADCAST);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_ALLOW_BROADCAST);
return 0;
#endif /* IPCOM_USE_INET */
#if defined(IP_SO_USELOOPBACK) && (defined(IPNET_USE_ROUTESOCK) || defined(IPNET_USE_NETLINKSOCK))
case IP_SO_USELOOPBACK:
if (sock->ipcom.domain != IP_AF_ROUTE && sock->ipcom.domain != IP_AF_NETLINK)
return -IP_ERRNO_ENOPROTOOPT;
if (val)
sock->bind_to_ifindex = 0;
else
sock->bind_to_ifindex = (Ip_u32)sock->ipcom.pid;
return 0;
#endif /* defined(IP_SO_USELOOPBACK) && (defined(IPNET_USE_ROUTESOCK) || defined(IPNET_USE_NETLINKSOCK)) */
#ifdef IPTCP
case IP_SO_LINGER:
{
IP_CONST struct Ip_linger *l = optval;
if(sock->tcb == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
if (l->l_onoff)
sock->tcb->sec_to_linger = l->l_linger;
else
sock->tcb->sec_to_linger = IPCOM_ADDR_INFINITE;
}
return 0;
#endif /* IPTCP */
#ifdef IPNET_USE_RFC2890
case IP_SO_X_GRE_KEY:
{
IP_CONST struct Ip_gre_key *gk = optval;
if (gk->gk_onoff)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_GRE_KEY);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_GRE_KEY);
sock->gre_key = gk->gk_key;
}
return 0;
#endif /* IPNET_USE_RFC2890 */
case IP_SO_BINDTODEVICE:
{
Ipnet_netif *netif;
netif = ipnet_if_nametonetif(sock->vr_index, (char *) optval);
if (netif == IP_NULL)
return -IP_ERRNO_ENXIO;
sock->bind_to_ifindex = netif->ipcom.ifindex;
return 0;
}
case IP_SO_X_EVENTCB_RO:
sock->ipcom.event_cb = *(Ipcom_socket_eventcb *)optval;
if (sock->ipcom.event_cb != IP_NULL)
return ipnet_receive_cb_init(sock);
return 0;
case IP_SO_SNDBUF:
if (val < 0)
return -IP_ERRNO_EINVAL;
sock->send_max_bytes = IP_MAX(IPNET_SOCKBUF_MULTIPLIER * val, IPNET_MIN_SNDBUF_SIZE);
return 0;
case IP_SO_RCVBUF:
if (val < 0)
return -IP_ERRNO_EINVAL;
sock->recv_max_bytes = IP_MAX(IPNET_SOCKBUF_MULTIPLIER * val, IPNET_MIN_RCVBUF_SIZE);
return 0;
case IP_SO_X_VR:
if (ipnet_has_priv(IPNET_PRIV_CHVR) == IP_FALSE)
return -IP_ERRNO_EPERM;
if (IP_BIT_ISSET(sock->flags,
IPNET_SOCKET_FLAG_BOUND | IPNET_SOCKET_FLAG_MROUTE_CONTROL))
/*
* Cannot change the virtual router when then socket has
* been bound since the vr affects which flow the socket
* will match
*/
return -IP_ERRNO_EACCES;
if (val < 0 || val > 0xffff)
return -IP_ERRNO_EINVAL;
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_REACHABLE))
sock->vr_index = (Ip_u16) val;
else
{
ipnet_sock_set_unreachable(sock);
sock->vr_index = (Ip_u16) val;
ipnet_sock_set_reachable(sock);
}
return 0;
case IP_SO_X_BYPASS_FLOW:
#ifdef IPCOM_USE_INET
if (sock->ip4 != IP_NULL)
{
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_BYPASS_FLOW);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_BYPASS_FLOW);
}
else
#endif
#ifdef IPCOM_USE_INET6
if (sock->ip6 != IP_NULL)
{
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_BYPASS_FLOW);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_BYPASS_FLOW);
}
else
#endif
return -IP_ERRNO_EINVAL;
break;
case IP_SO_X_COOKIE:
sock->ipcom.cookie = *(void**) optval;
return 0;
case IP_SO_X_PROBEDELAY:
sock->keep_alive_probe_delay = val;
return 0;
case IP_SO_RCVTIMEO:
if (sock->rcv_timeout == IP_NULL)
{
sock->rcv_timeout = ipcom_malloc(optlen);
if (sock->rcv_timeout == IP_NULL)
return -IP_ERRNO_ENOMEM;
}
ipcom_memcpy(sock->rcv_timeout, optval, optlen);
if (sock->rcv_timeout->tv_sec == 0
&& sock->rcv_timeout->tv_usec == 0)
{
/*
* Infinite receive timeout.
*/
ipcom_free(sock->rcv_timeout);
sock->rcv_timeout = IP_NULL;
}
return 0;
#ifdef IPNET_USE_SO_CONNTIMEO
case IP_SO_CONNTIMEO:
if (sock->conn_timeout == IP_NULL)
{
sock->conn_timeout = ipcom_malloc(optlen);
if (sock->conn_timeout == IP_NULL)
return -IP_ERRNO_ENOMEM;
}
ipcom_memcpy(sock->conn_timeout, optval, optlen);
if (sock->conn_timeout->tv_sec == 0
&& sock->conn_timeout->tv_usec == 0)
{
ipcom_free(sock->conn_timeout);
sock->conn_timeout = IP_NULL;
}
return 0;
#endif
#ifdef IPNET_USE_PER_SOCKET_VLAN_TAG
case IP_SO_VLAN:
{
IP_CONST struct Ip_sovlan *sovlan = optval;
if (!sovlan->vlan_onoff)
{
sock->vlan_tag = 0;
return 0;
}
if (sovlan->upriority > 7
|| (!sovlan->priority_tagged && sovlan->vid > 4094))
return -IP_ERRNO_EINVAL;
sock->vlan_tag = (sovlan->upriority << 13);
if (!sovlan->priority_tagged)
sock->vlan_tag |= sovlan->vid;
return 0;
}
#endif /* IPNET_USE_PER_SOCKET_VLAN_TAG */
/*
* Socket flags controlled by setsockopt
*/
case IP_UDP_X_CHKSUM:
flag = IPNET_SOCKET_FLAG_ADDCHECKSUM;
break;
case IP_SO_REUSEADDR:
flag = IPNET_SOCKET_FLAG_REUSEADDR;
break;
case IP_SO_REUSEPORT:
flag = IPNET_SOCKET_FLAG_REUSEPORT;
break;
case IP_SO_DONTROUTE:
flag = IPNET_SOCKET_FLAG_DONTROUTE;
break;
case IP_SO_KEEPALIVE:
flag = IPNET_SOCKET_FLAG_KEEPALIVE;
break;
case IP_SO_OOBINLINE:
flag = IPNET_SOCKET_FLAG_OOBINLINE;
break;
case IP_SO_X_DRAINABLE:
flag = IPNET_SOCKET_FLAG_DRAINABLE;
break;
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
#ifdef IPNET_SO_WAKEUP_HOOK
case IP_SO_X_WINDNET_SO:
sock->windnet_so = *(void**) optval;
return 0;
#endif
#endif
case IP_SO_TIMESTAMPING:
flag = IPNET_SOCKET_FLAG_TIMESTAMPING;
sock->timestamp_flag = val;
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
if (val)
IP_BIT_SET(sock->flags, flag);
else
IP_BIT_CLR(sock->flags, flag);
if (optname == IP_SO_REUSEPORT || optname == IP_SO_REUSEADDR)
ipnet_sock_update_reuse(sock);
#ifdef IPTCP
if (optname == IP_SO_KEEPALIVE
&& sock->tcb != IP_NULL)
sock->notify_cb(sock, IPTCP_SOCKET_EVENT_KEEP_ALIVE);
#endif
return 0;
}
/*
*===========================================================================
* ipnet_getsockopt_udp
*===========================================================================
* Description: Socket option where level was set to IP_IPPROTO_UDP or
* IP_IPPROTO_UDPLITE.
* Parameters:
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_getsockopt_udp(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
int val = 0;
if (sock->proto == IP_IPPROTO_UDPLITE)
{
if (optname == IP_UDPLITE_SEND_CSCOV
&& *optlen >= sizeof(int))
{
*(int*) optval = sock->udplite_sum_cov;
*optlen = sizeof(int);
return 0;
}
return -IP_ERRNO_EINVAL;
}
if (sock->proto == IP_IPPROTO_UDP)
{
switch (optname)
{
case IP_UDP_NAT_T:
val = sock->udp_encapsulation;
break;
/* Fall through */
default:
goto sol_socket;
}
if (*optlen >= sizeof(int))
{
*(int*) optval = val;
*optlen = sizeof(int);
return 0;
}
}
sol_socket:
/*
* Invoke the socket level option
*/
return ipnet_getsockopt_sol(sock,optname,optval,optlen);
}
/*
*===========================================================================
* ipnet_setsockopt_udp
*===========================================================================
* Description: Socket option where level was set to IP_IPPROTO_UDP or
* IP_IPPROTO_UDPLITE.
* Parameters:
* Returns: 0 = success, <0 error code.
*
*/
IP_STATIC int
ipnet_setsockopt_udp(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
int val;
Ip_socklen_t this_optlen;
if (sock->proto == IP_IPPROTO_UDPLITE)
{
if (optname == IP_UDPLITE_SEND_CSCOV
&& optlen == sizeof(int))
{
val = *(int*) optval;
if (val == 0 || val >= 8)
{
sock->udplite_sum_cov = val;
return 0;
}
/*
* else: Checksum must cover the whole UDPLite header so
* values 1..7 are invald
*/
}
return -IP_ERRNO_EINVAL;
}
if (sock->proto == IP_IPPROTO_UDP)
{
/* Verify optlen size */
switch (optname)
{
default:
this_optlen = sizeof(int);
break;
}
/* Does the argument match? */
if (optlen == this_optlen)
{
val = *(int*) optval;
/* Process option */
switch (optname)
{
case IP_UDP_NAT_T:
switch (val)
{
case IP_UDP_NAT_T_DISABLED:
sock->udp_encapsulation = IP_UDP_NAT_T_DISABLED;
break;
#ifdef IPCOM_USE_INET
case IP_UDP_NAT_T_RFC3519:
sock->udp_encapsulation = IP_UDP_NAT_T_RFC3519;
break;
case IP_UDP_NAT_T_RFC3948:
sock->udp_encapsulation = IP_UDP_NAT_T_RFC3948;
break;
#endif
case IP_UDP_NAT_T_DSMIPV6:
sock->udp_encapsulation = IP_UDP_NAT_T_DSMIPV6;
break;
default:
return -IP_ERRNO_EINVAL;
}
return 0;
}
}
}
/* SOL_SOCKET socket option */
return ipnet_setsockopt_sol(sock, optname, optval, optlen);
}
#ifdef IPCOM_USE_INET
/*
*===========================================================================
* ipnet_getsockopt_ip4
*===========================================================================
* Description: Socket option where level was set to IP_IPPROTO_IP.
* Parameters:
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_getsockopt_ip4(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
int val;
Ip_u8 val_u8;
Ipnet_netif *netif;
#ifdef IPNET_USE_MCAST_ROUTING
if (optname & IP_MRT_BASE)
return ipnet_ip4_mcast_getsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_MCAST_ROUTING */
#ifdef IPNET_USE_NAT
if (optname == IP_IP_NAT)
return (int)ipnet_nat_getsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_NAT */
#ifdef IPFIREWALL
if (optname == IP_IP_FW)
{
if (ipfirewall == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
return (int)ipfirewall_getsockopt(sock, optname, optval, optlen);
}
#endif /* IPFIREWALL */
switch (optname)
{
case IP_IP_OPTIONS:
if (sock->ip4->opts == IP_NULL)
{
*optlen = 0;
return 0;
}
if (sock->ip4->opts->len > (int) *optlen)
return -IP_ERRNO_EINVAL;
*optlen = sock->ip4->opts->len;
ipcom_memcpy(optval, &sock->ip4->opts->opts, *optlen);
return 0;
case IP_IP_HDRINCL:
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_HDRINCL);
break;
case IP_IP_TOS:
val = sock->ip4->type_of_service;
break;
case IP_IP_RECVTOS:
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTOS);
break;
case IP_IP_TTL:
val = sock->uni_hop_limit;
break;
case IP_IP_RECVTTL:
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTTL);
break;
case IP_IP_RECVDSTADDR:
val = (int) IP_BIT_ISTRUE(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVDSTADDR);
break;
case IP_IP_PKTINFO:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
break;
#ifdef IPNET_USE_ROUTER_ALERT
case IP_IP_ROUTER_ALERT:
if (ipnet_sock_join_router_alert_chain(sock) < 0)
/*
* Already joined
*/
val_u8 = IP_TRUE;
else
{
ipnet_sock_leave_router_alert_chain(sock);
val_u8 = IP_FALSE;
}
goto u8_val;
#endif /* IPCOM_USE_ROUTER_ALERT */
case IP_IP_DONTFRAG:
val_u8 = sock->ip4->dont_frag;
goto u8_val;
case IP_IP_NOLOCALFRAG:
val = sock->ip4->no_local_frag;
break;
case IP_IP_MULTICAST_TTL:
if (*optlen < sizeof(int))
{
val_u8 = (Ip_u8)sock->multi_hop_limit;
goto u8_val;
}
else
val = (int)sock->multi_hop_limit;
break;
case IP_IP_MULTICAST_LOOP:
val_u8 = (Ip_u8) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
u8_val:
if (*optlen >= sizeof(Ip_u8))
{
*(Ip_u8 *) optval = val_u8;
*optlen = sizeof(Ip_u8);
return 0;
}
return -IP_ERRNO_EINVAL;
case IP_IP_RECVIF:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
break;
case IP_IP_X_SENDERLINKADDR:
val = (int) IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
break;
case IP_IP_MULTICAST_IF:
#ifndef IP_PORT_LKM
if (*optlen < sizeof(struct Ip_in_addr))
return -IP_ERRNO_EINVAL;
*optlen = sizeof(struct Ip_in_addr);
netif = ipnet_if_indextonetif(sock->vr_index, sock->ip4->multicast_if);
if (netif == IP_NULL || netif->inet4_addr_list == IP_NULL)
ipcom_memset(optval, 0, sizeof(struct Ip_in_addr));
else
ipcom_memcpy(optval, &netif->inet4_addr_list->ipaddr_n, sizeof(struct Ip_in_addr));
#else
/* Linux uses the Ip_mreqn struct for this */
if (*optlen < sizeof(struct Ip_ip_mreqn))
return -IP_ERRNO_EINVAL;
*optlen = sizeof(struct Ip_ip_mreqn);
netif = ipnet_if_indextonetif(sock->vr_index, sock->ip4->multicast_if);
if (netif == IP_NULL || netif->inet4_addr_list == IP_NULL)
ipcom_memset(optval, 0, sizeof(struct Ip_ip_mreqn));
else
{
struct Ip_ip_mreqn *mreqn = (struct Ip_ip_mreqn*)optval;
ipcom_memcpy(&mreqn->imr_interface,
&netif->inet4_addr_list->ipaddr_n,
sizeof(struct Ip_in_addr));
mreqn->imr_multiaddr.s_addr = IP_INADDR_ANY;
mreqn->imr_ifindex = netif->ipcom.ifindex;
}
#endif
return 0;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
if (*optlen >= sizeof(int))
{
*(int*) optval = val;
*optlen = sizeof(int);
return 0;
}
return -IP_ERRNO_EINVAL;
}
/*
*===========================================================================
* ipnet_sockopt_ip4_mcast_create_data
*===========================================================================
* Description: Creates a socket control block for one multicast address
* controlled via the socket.
* Parameters: sock - The controlling socket for the multicast address.
* netif - The interface the multicast address will be assigned to.
* maddr - An multicast address.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_sockopt_ip4_mcast_create_data(Ipnet_socket *sock,
Ipnet_netif *netif,
struct Ip_in_addr *maddr)
{
Ipnet_ip4_sock_mcast_data **pstorage;
Ipnet_ip4_sock_mcast_data *mcast_data;
int ret;
if (IP_IN_CLASSD(maddr->s_addr) == IP_FALSE)
return -IP_ERRNO_EINVAL;
pstorage = ipnet_sock_ip4_get_mcast_data(sock, netif, maddr);
if (*pstorage != IP_NULL)
/* The group is already joined */
return -IP_ERRNO_EADDRINUSE;
mcast_data = ipcom_malloc(sizeof(Ipnet_ip4_sock_mcast_data));
if (mcast_data == IP_NULL)
return -IP_ERRNO_ENOMEM;
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
mcast_data->sources = ipcom_set_new(sizeof(struct Ip_in_addr));
if (mcast_data->sources == IP_NULL)
{
ipcom_free(mcast_data);
return -IP_ERRNO_ENOMEM;
}
mcast_data->sources->user = IPCOM_SET_IS_EXCLUDE;
mcast_data->local_netif = IP_NULL;
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
mcast_data->netif = netif;
mcast_data->maddr.s_addr = maddr->s_addr;
mcast_data->next = IP_NULL;
*pstorage = mcast_data;
ret = ipnet_ip4_add_addr(netif,
maddr->s_addr,
IP_IN_CLASSD_NET,
IP_FALSE,
IPNET_ADDR_TYPE_MULTICAST);
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
if (ret >= 0)
{
Ipnet_ip4_addr_entry *addr_entry;
addr_entry = ipnet_ip4_get_addr_entry(maddr->s_addr, sock->vr_index, netif);
if (addr_entry == IP_NULL)
ret = -IP_ERRNO_ENXIO;
else if (ipcom_set_add(addr_entry->mcast.sockets, &sock) != IPCOM_SUCCESS)
ret = -IP_ERRNO_ENOMEM;
}
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
if (ret < 0)
(void)ipnet_sock_ip4_mcast_delete_data(sock, netif, maddr);
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
else
ipnet_igmpv3_report_change(netif, maddr);
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
return ret;
}
/*
*===========================================================================
* ipnet_sockopt_ip4_mcast_add_remove_source
*===========================================================================
* Description: Adds or removes a multicast channel.
* Parameters: sock - The socket that contains the
* netif - The network interface the multicast address is assigned to.
* group - The group to add/remove.
* source - The source address to add/remove.
* filter_mode - IPCOM_SET_IS_INCLUDE or IPCOM_SET_IS_EXCLUDE
* is_add - IP_TRUE if the source address should be added.
* Returns: >=0 = success, <0 = error code.
*
*/
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
IP_STATIC int
ipnet_sockopt_ip4_mcast_add_remove_source(Ipnet_socket *sock,
Ipnet_netif *netif,
struct Ip_in_addr *group,
struct Ip_in_addr *source,
int filter_mode,
Ip_bool is_add)
{
int ret = 0;
Ipnet_ip4_sock_mcast_data *mcast_data;
mcast_data = *ipnet_sock_ip4_get_mcast_data(sock, netif, group);
if (mcast_data == IP_NULL)
{
if (is_add && filter_mode == IPCOM_SET_IS_INCLUDE)
{
/* Create a new data entry for the multicast address */
ret = ipnet_sockopt_ip4_mcast_create_data(sock, netif, group);
if (ret < 0)
return ret;
mcast_data = *ipnet_sock_ip4_get_mcast_data(sock, netif, group);
mcast_data->sources->user = filter_mode;
}
if (mcast_data == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
}
if (mcast_data->sources->user != filter_mode)
return -IP_ERRNO_EINVAL;
if (!is_add)
{
if (ipcom_set_remove(mcast_data->sources, source) != IPCOM_SUCCESS)
ret = -IP_ERRNO_EADDRNOTAVAIL;
else if (filter_mode == IPCOM_SET_IS_INCLUDE
&& mcast_data->sources->obj_count == 0)
{
/* Not member of any channels that contains this multicast address */
ret = ipnet_sock_ip4_mcast_delete_data(sock,
netif,
group);
}
}
else
{
switch (ipcom_set_add(mcast_data->sources, source))
{
case IPCOM_ERR_ALREADY_INSTALLED:
ret = -IP_ERRNO_EADDRNOTAVAIL;
break;
case IPCOM_ERR_NO_MEMORY:
ret = -IP_ERRNO_ENOMEM;
break;
default:
break;
}
}
if (ret == 0)
{
ipnet_igmpv3_report_change(netif, group);
IPCOM_LOG5(INFO, "IPv4: %s source %s to group %s at interface %s, filter mode is %s",
is_add ? "added" : "removed",
ipcom_inet_ntop(IP_AF_INET, source, ipnet->log_buf, sizeof(ipnet->log_buf)),
ipcom_inet_ntop(IP_AF_INET, group, ipnet->log_buf + IP_INET_ADDRSTRLEN, sizeof(ipnet->log_buf)),
netif->ipcom.name,
filter_mode == IPCOM_SET_IS_INCLUDE ? "include" : "exclude");
}
return ret;
}
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
/*
*===========================================================================
* ipnet_sockopt_ip4_set_ip_options
*===========================================================================
* Description: Checks the IP options for sanity and copies them to the
* socket buffer.
* Parameters: opts - The socket buffer where the options will be stored.
* optval - User supplied options.
* optlen - The length of the user supplied options.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_sockopt_ip4_set_ip_options(struct Ipnet_ip4_sock_opts *o,
IP_CONST Ip_u8 *optval,
Ip_socklen_t optlen)
{
Ip_u8 olen, j;
Ip_socklen_t i;
for (i = 0;
i < optlen && optval[i] != IP_IPOPT_END;
i += olen)
{
switch (optval[i])
{
case IP_IPOPT_NOOP:
olen = 1;
break;
default:
if (optlen < i + 1)
return -IP_ERRNO_EINVAL;
olen = optval[i + 1];
if (olen < 2 || i + olen > optlen)
return -IP_ERRNO_EINVAL;
break;
}
switch (optval[i])
{
case IP_IPOPT_RR:
/*
* RFC 791, page 21:
* The pointer is relative to this option, and the
* smallest legal value for the pointer is 4
*/
if (optval[i + 2] < 4)
return -IP_ERRNO_EINVAL;
/*
* RFC 791, page 21:
* The intitial contents of the route data area must be zero.
*/
for (j=3; j<olen; j++)
if (optval[i + j] != 0)
return -IP_ERRNO_EINVAL;
break;
default:
break;
}
}
ipcom_memset(o->opts, IP_IPOPT_END, sizeof(o->opts));
ipcom_memcpy(o->opts, optval, i);
o->len = IP_ROUNDUP(i, 4);
return 0;
}
/*
*===========================================================================
* ipnet_sockopt_ip4_opt_hdr_space
*===========================================================================
* Description: Returns the number of extra octets that must be
* reserved at the beginning of the packet when IP
* options is included.
* Parameters: sock -
* msg -
* Returns:
*
*/
IP_STATIC Ip_size_t
ipnet_sockopt_ip4_opt_hdr_space(Ipnet_socket *sock,
const struct Ip_msghdr *msg)
{
IPCOM_UNUSED_ARG(sock);
IPCOM_UNUSED_ARG(msg);
/*
* Always assume the worst case to make it very simple...
*/
return 40;
}
/*
*===========================================================================
* ipnet_setsockopt_ip4_update_sticky_option_flag
*===========================================================================
* Description: Sets the sticky option flag if sticky option(s) is
* present, clears the flag otherwise.
* Parameters: sock - The socket on which the option should be set.
* optname - The option to set.
* optval - The value to set.
* optlen - The length of the buffer pointed to by optval.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC void
ipnet_setsockopt_ip4_update_sticky_option_flag(Ipnet_socket *sock)
{
if (sock->ip4->opts)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_HAS_STICKY_OPT);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_HAS_STICKY_OPT);
}
/*
*===========================================================================
* ipnet_setsockopt_ip4
*===========================================================================
* Description: Socket option where level was set to IP_IPPROTO_IP.
* Parameters:
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_setsockopt_ip4(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
int val = 0;
struct Ip_ip_mreq *mreq;
struct Ip_group_source_req *gsreq;
Ipnet_netif *netif;
struct Ip_sockaddr_in *group;
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
struct Ip_sockaddr_in *source;
#endif
#ifdef IPNET_USE_MCAST_ROUTING
if (optname & IP_MRT_BASE)
return ipnet_ip4_mcast_setsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_MCAST_ROUTING */
#ifdef IPNET_USE_NAT
if (optname == IP_IP_NAT)
return (int)ipnet_nat_setsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_NAT */
#ifdef IPFIREWALL
if (optname == IP_IP_FW)
{
if (ipfirewall == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
return (int)ipfirewall_setsockopt(sock, optname, optval, optlen);
}
#endif /* IPFIREWALL */
switch (optname)
{
case IP_IP_OPTIONS:
if (optlen > 40)
return -IP_ERRNO_EINVAL;
break;
case IP_IP_MULTICAST_LOOP:
case IP_IP_ROUTER_ALERT:
if (optlen == sizeof(int))
{
/* These options is an int on some platforms */
val = *(int*) optval;
break;
}
/* else, fall through */
case IP_IP_DONTFRAG:
if (optlen != sizeof(Ip_u8))
return -IP_ERRNO_EINVAL;
val = (int) (*(Ip_u8 *) optval);
break;
case IP_IP_MULTICAST_TTL:
/*
* Set the IP time-to-live for outgoing multicast packets.
* The original multicast API required a char argument,
* which is inconsistent with the rest of the socket API.
* We allow either a char or an int.
*/
if (optlen >= sizeof(int))
val = *(int *) optval;
else if (optlen >= sizeof(Ip_u8))
val = (int) (*(Ip_u8 *) optval);
else
return -IP_ERRNO_EINVAL;
break;
case IP_IP_HDRINCL:
case IP_IP_NOLOCALFRAG:
case IP_IP_PKTINFO:
case IP_IP_RECVDSTADDR:
case IP_IP_RECVIF:
case IP_IP_RECVTOS:
case IP_IP_RECVTTL:
case IP_IP_TOS:
case IP_IP_TTL:
case IP_IP_X_SENDERLINKADDR:
if (optlen != sizeof(int))
return -IP_ERRNO_EINVAL;
val = *(int*) optval;
break;
default:
break;
}
switch (optname)
{
case IP_IP_OPTIONS:
if (optlen == 0)
{
ipcom_free(sock->ip4->opts);
sock->ip4->opts = IP_NULL;
break;
}
if (sock->ip4->opts == IP_NULL)
{
sock->ip4->opts = ipcom_malloc(sizeof(struct Ipnet_ip4_sock_opts));
if (sock->ip4->opts == IP_NULL)
return -IP_ERRNO_ENOMEM;
}
val = ipnet_sockopt_ip4_set_ip_options(sock->ip4->opts, optval, optlen);
if (val < 0)
return val;
/*
* Reserve extra header space in each packet to make room for
* the IP-options
*/
sock->dynamic_header_space = ipnet_sockopt_ip4_opt_hdr_space;
break;
case IP_IP_TOS:
if (val == -1)
sock->ip4->type_of_service = 0;
else if (val < 0 || val > 0xff)
return -IP_ERRNO_EINVAL;
else
sock->ip4->type_of_service = (Ip_u8) val;
break;
case IP_IP_RECVTOS:
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTOS);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTOS);
break;
case IP_IP_TTL:
if (val == -1)
sock->uni_hop_limit = (Ip_u8) ipcom_sysvar_get_as_int0("ipnet.inet.BaseHopLimit", 64);
else if (val >= 0 && val <= 255)
sock->uni_hop_limit = (Ip_u8)val;
else
return -IP_ERRNO_EINVAL;
break;
case IP_IP_RECVTTL:
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTTL);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVTTL);
break;
case IP_IP_HDRINCL:
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_HDRINCL);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_HDRINCL);
break;
case IP_IP_RECVIF:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
break;
case IP_IP_X_SENDERLINKADDR:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
break;
case IP_IP_RECVDSTADDR:
if (val)
IP_BIT_SET(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVDSTADDR);
else
IP_BIT_CLR(sock->ip4->flags, IPNET_SOCKET_FLAG_IP4_RECVDSTADDR);
break;
case IP_IP_PKTINFO:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
break;
case IP_IP_MULTICAST_TTL:
if (val > 0)
sock->multi_hop_limit = (Ip_u8) val;
else
sock->multi_hop_limit = 1;
break;
case IP_IP_MULTICAST_LOOP:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
break;
case IP_IP_MULTICAST_IF:
{
struct Ip_in_addr *ifaddr;
if (optlen < sizeof(struct Ip_in_addr))
return -IP_ERRNO_EINVAL;
/* Use the size to determine what we're dealing with */
if (optlen == sizeof(struct Ip_ip_mreqn))
{
/* This is a new 'linux' style mreqn */
struct Ip_ip_mreqn *mreqn = (struct Ip_ip_mreqn *) optval;
/* Try resolve using the ifindex if specified */
if (mreqn->imr_ifindex != 0)
{
/* It' set; if we fail to resolve the interface, exit. */
netif = ipnet_if_indextonetif(sock->vr_index, mreqn->imr_ifindex);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
/* Set the index */
sock->ip4->multicast_if = netif->ipcom.ifindex;
break;
}
/* Store address */
ifaddr = &mreqn->imr_interface;
}
else if (optlen == sizeof(struct Ip_ip_mreq))
{
/* This is an old BSD style mreq */
mreq = (struct Ip_ip_mreq *) optval;
ifaddr = &mreq->imr_interface;
}
else
{
/* This is a BSD style address only argument */
ifaddr = (struct Ip_in_addr *) optval;
}
/* Resolve using the provided address */
if (ifaddr->s_addr == IP_INADDR_ANY)
sock->ip4->multicast_if = 0;
else
{
netif = ipnet_sockopt_ip4_addr_to_netif(sock->vr_index, ifaddr);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
sock->ip4->multicast_if = netif->ipcom.ifindex;
}
}
break;
case IP_IP_JOIN_GROUP:
case IP_IP_LEAVE_GROUP:
if (optlen < sizeof(struct Ip_ip_mreq))
return -IP_ERRNO_EINVAL;
mreq = (struct Ip_ip_mreq *) optval;
/* Is it a new structure containing the ifindex? */
netif = IP_NULL;
if (optlen == sizeof(struct Ip_ip_mreqn))
{
struct Ip_ip_mreqn *mreqn = (struct Ip_ip_mreqn *) mreq;
/* Try resolve using the ifindex if specified */
if (mreqn->imr_ifindex != 0)
{
/* It' set; if we fail to resolve the interface, exit. */
netif = ipnet_if_indextonetif(sock->vr_index, mreqn->imr_ifindex);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
}
}
/* Resolve using the interface */
if (netif == IP_NULL)
netif = ipnet_sockopt_ip4_addr_to_netif(sock->vr_index, &mreq->imr_interface);
if (netif != IP_NULL)
{
struct Ip_group_req gr_req;
int ret;
ipcom_memset(&gr_req, 0, sizeof(gr_req));
gr_req.gr_interface = netif->ipcom.ifindex;
group = (struct Ip_sockaddr_in *) &gr_req.gr_group;
IPCOM_SA_LEN_SET(group, sizeof(gr_req.gr_group));
group->sin_family = IP_AF_INET;
group->sin_addr = mreq->imr_multiaddr;
ret = ipnet_setsockopt_ip4(sock,
optname == IP_IP_JOIN_GROUP ? IP_MCAST_JOIN_GROUP : IP_MCAST_LEAVE_GROUP,
&gr_req,
sizeof(gr_req));
if (ret < 0)
return ret;
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
if (group->sin_addr.s_addr != IP_INADDR_ANY
&& mreq->imr_interface.s_addr != IP_INADDR_ANY
&& optname == IP_IP_JOIN_GROUP)
{
Ipnet_ip4_sock_mcast_data *mcd;
mcd = *ipnet_sock_ip4_get_mcast_data(sock,
netif,
&group->sin_addr);
ip_assert(mcd != IP_NULL);
mcd->local_netif = netif;
}
#endif
return 0;
}
return -IP_ERRNO_EADDRNOTAVAIL;
case IP_MCAST_JOIN_GROUP:
case IP_MCAST_LEAVE_GROUP:
gsreq = (struct Ip_group_source_req *) optval;
if (optlen < sizeof(struct Ip_group_req)
|| gsreq->gsr_group.ss_family != IP_AF_INET
|| IPCOM_SA_LEN_GET(&gsreq->gsr_group) < sizeof(struct Ip_sockaddr_in))
return -IP_ERRNO_EINVAL;
netif = ipnet_if_indextonetif(sock->vr_index, gsreq->gsr_interface);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
group = (struct Ip_sockaddr_in *) &gsreq->gsr_group;
if (group->sin_addr.s_addr == IP_INADDR_ANY)
return ipnet_sockopt_enter_exit_promisc(sock, netif, optname == IP_MCAST_JOIN_GROUP);
if (optname == IP_MCAST_LEAVE_GROUP)
{
return ipnet_sock_ip4_mcast_delete_data(sock, netif, &group->sin_addr);
}
else
{
Ipnet_ip4_addr_entry *addr;
int ret;
ret = ipnet_sockopt_ip4_mcast_create_data(sock, netif, &group->sin_addr);
if (ret >= 0)
{
addr = ipnet_ip4_get_addr_entry(group->sin_addr.s_addr, sock->vr_index, netif);
if (addr == IP_NULL)
ret = -IP_ERRNO_ENXIO;
else
ipnet_igmp_report_filter_change_start(addr);
}
return ret;
}
return ipnet_sockopt_ip4_mcast_create_data(sock, netif, &group->sin_addr);
case IP_MCAST_BLOCK_SOURCE:
case IP_MCAST_UNBLOCK_SOURCE:
case IP_MCAST_JOIN_SOURCE_GROUP:
case IP_MCAST_LEAVE_SOURCE_GROUP:
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
gsreq = (struct Ip_group_source_req *) optval;
if (optlen < sizeof(struct Ip_group_source_req)
|| gsreq->gsr_group.ss_family != IP_AF_INET
|| IPCOM_SA_LEN_GET(&gsreq->gsr_group) < sizeof(struct Ip_sockaddr_in)
|| gsreq->gsr_source.ss_family != IP_AF_INET
|| IPCOM_SA_LEN_GET(&gsreq->gsr_source) < sizeof(struct Ip_sockaddr_in))
return -IP_ERRNO_EINVAL;
netif = ipnet_if_indextonetif(sock->vr_index, gsreq->gsr_interface);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
group = (struct Ip_sockaddr_in *) &gsreq->gsr_group;
source = (struct Ip_sockaddr_in *) &gsreq->gsr_source;
return ipnet_sockopt_ip4_mcast_add_remove_source(sock,
netif,
&group->sin_addr,
&source->sin_addr,
(optname == IP_MCAST_BLOCK_SOURCE
|| optname == IP_MCAST_UNBLOCK_SOURCE
? IPCOM_SET_IS_EXCLUDE
: IPCOM_SET_IS_INCLUDE),
(optname == IP_MCAST_BLOCK_SOURCE
|| optname == IP_MCAST_JOIN_SOURCE_GROUP));
#else
return -IP_ERRNO_EOPNOTSUPP;
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
#ifdef IPNET_USE_ROUTER_ALERT
case IP_IP_ROUTER_ALERT:
if (sock->ipcom.type != IP_SOCK_RAW)
/*
* Is only allowed for RAW sockets
*/
return -IP_ERRNO_EPERM;
if (val)
/*
* Should join the router alert chain
*/
return ipnet_sock_join_router_alert_chain(sock);
return ipnet_sock_leave_router_alert_chain(sock);
#endif /* IPNET_USE_ROUTER_ALERT */
case IP_IP_DONTFRAG:
sock->ip4->dont_frag = (Ip_u8) (val != 0);
break;
case IP_IP_NOLOCALFRAG:
sock->ip4->no_local_frag = (Ip_u8) (val != 0);
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
ipnet_setsockopt_ip4_update_sticky_option_flag(sock);
return 0;
}
/*
*===========================================================================
* ipnet_getsockopt_icmp
*===========================================================================
* Description: Get socket option where level was was set to IP_SOL_RAW and
* socket protocol is IP_IPPROTO_ICMP.
* Parameters: sock - The socket on which the option should be get.
* optname - The option to get.
* optval - The buffer into which the value should be copied.
* optlen - The length of the buffer pointed to by optval.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_getsockopt_icmp(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
switch (optname)
{
case IP_ICMP_FILTER:
if (*optlen < sizeof(struct Ip_icmp_filter))
return -IP_ERRNO_EINVAL;
*optlen = sizeof(struct Ip_icmp_filter);
ipcom_memcpy(optval, sock->ip4->icmp_filter, sizeof(struct Ip_icmp_filter));
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
return 0;
}
/*
*===========================================================================
* ipnet_setsockopt_icmp
*===========================================================================
* Description: Set socket option where level was set to IP_SOL_RAW and
* socket protocol is IP_IPPROTO_ICMP..
* Parameters: sock - The socket on which the option should be set.
* optname - The option to set.
* optval - The value to set.
* optlen - The length of the buffer pointed to by optval.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_setsockopt_icmp(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
switch (optname)
{
case IP_ICMP_FILTER:
if (optlen != sizeof(struct Ip_icmp_filter))
return -IP_ERRNO_EINVAL;
ipcom_memcpy(sock->ip4->icmp_filter, optval, optlen);
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
return 0;
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
/*
*===========================================================================
* ipnet_sockopt_ip6_mcast_create_data
*===========================================================================
* Description: Creates a socket control block for one multicast address
* controlled via the socket.
* Parameters: sock - The controlling socket for the multicast address.
* netif - The interface the multicast address will be assigned to.
* maddr - An multicast address.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_sockopt_ip6_mcast_create_data(Ipnet_socket *sock,
Ipnet_netif *netif,
struct Ip_in6_addr *maddr)
{
Ipnet_ip6_sock_mcast_data **pstorage;
Ipnet_ip6_sock_mcast_data *mcast_data;
int ret;
if (IP_IN6_IS_ADDR_MULTICAST(maddr) == IP_FALSE)
return -IP_ERRNO_EINVAL;
pstorage = ipnet_sock_ip6_get_mcast_data(sock, netif, maddr);
if (*pstorage != IP_NULL)
/* The group is already joined */
return -IP_ERRNO_EADDRINUSE;
mcast_data = ipcom_malloc(sizeof(Ipnet_ip6_sock_mcast_data));
if (mcast_data == IP_NULL)
return -IP_ERRNO_ENOMEM;
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
mcast_data->sources = ipcom_set_new(sizeof(struct Ip_in6_addr));
if (mcast_data->sources == IP_NULL)
{
ipcom_free(mcast_data);
return -IP_ERRNO_ENOMEM;
}
mcast_data->sources->user = IPCOM_SET_IS_EXCLUDE;
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
mcast_data->netif = netif;
mcast_data->maddr = *maddr;
mcast_data->next = IP_NULL;
*pstorage = mcast_data;
ret = ipnet_ip6_add_addr(netif,
maddr,
0,
IPCOM_ADDR_INFINITE,
IPCOM_ADDR_INFINITE,
16);
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
if (ret >= 0)
{
Ipnet_ip6_addr_entry *addr_entry;
addr_entry = ipnet_ip6_get_addr_entry(maddr, sock->vr_index, netif);
if (addr_entry == IP_NULL)
ret = -IP_ERRNO_ENXIO;
else if (ipcom_set_add(addr_entry->mcast.sockets, &sock) != IPCOM_SUCCESS)
ret = -IP_ERRNO_ENOMEM;
}
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
if (ret < 0)
(void)ipnet_sock_ip6_mcast_delete_data(sock, netif, maddr);
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
else
ipnet_mldv2_report_change(netif, maddr);
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
return ret;
}
/*
*===========================================================================
* ipnet_sockopt_ip6_mcast_add_remove_source
*===========================================================================
* Description: Adds or removes a multicast channel.
* Parameters: sock - The socket that contains the
* netif - The network interface the multicast address is assigned to.
* group - The group to add/remove.
* source - The source address to add/remove.
* filter_mode - IPCOM_SET_IS_INCLUDE or IPCOM_SET_IS_EXCLUDE
* is_add - IP_TRUE if the source address should be added.
* Returns: >=0 = success, <0 = error code.
*
*/
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
IP_STATIC int
ipnet_sockopt_ip6_mcast_add_remove_source(Ipnet_socket *sock,
Ipnet_netif *netif,
struct Ip_in6_addr *group,
struct Ip_in6_addr *source,
int filter_mode,
Ip_bool is_add)
{
int ret = 0;
Ipnet_ip6_sock_mcast_data *mcast_data;
mcast_data = *ipnet_sock_ip6_get_mcast_data(sock, netif, group);
if (mcast_data == IP_NULL)
{
if (is_add && filter_mode == IPCOM_SET_IS_INCLUDE)
{
/* Create a new data entry for the multicast address */
ret = ipnet_sockopt_ip6_mcast_create_data(sock, netif, group);
if (ret < 0)
return ret;
mcast_data = *ipnet_sock_ip6_get_mcast_data(sock, netif, group);
mcast_data->sources->user = filter_mode;
}
if (mcast_data == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
}
if (mcast_data->sources->user != filter_mode)
return -IP_ERRNO_EINVAL;
if (!is_add)
{
if (ipcom_set_remove(mcast_data->sources, source) != IPCOM_SUCCESS)
ret = -IP_ERRNO_EADDRNOTAVAIL;
else if (filter_mode == IPCOM_SET_IS_INCLUDE
&& mcast_data->sources->obj_count == 0)
{
/* Not member of any channels that contains this multicast address */
ret = ipnet_sock_ip6_mcast_delete_data(sock,
netif,
group);
}
}
else
{
switch (ipcom_set_add(mcast_data->sources, source))
{
case IPCOM_ERR_ALREADY_INSTALLED:
ret = -IP_ERRNO_EADDRNOTAVAIL;
break;
case IPCOM_ERR_NO_MEMORY:
ret = -IP_ERRNO_ENOMEM;
break;
default:
break;
}
}
if (ret == 0)
{
ipnet_mldv2_report_change(netif, group);
IPCOM_LOG5(INFO, "IPv6: %s source %s to group %s at interface %s, filter mode is %s",
is_add ? "added" : "removed",
ipcom_inet_ntop(IP_AF_INET6, source, ipnet->log_buf, sizeof(ipnet->log_buf)),
ipcom_inet_ntop(IP_AF_INET6, group, ipnet->log_buf + IP_INET6_ADDRSTRLEN, sizeof(ipnet->log_buf)),
netif->ipcom.name,
filter_mode == IPCOM_SET_IS_INCLUDE ? "include" : "exclude");
}
return ret;
}
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
/*
*===========================================================================
* ipnet_getsockopt_ip6
*===========================================================================
* Description: Get socket option where level was set to IP_IPPROTO_IPV6.
* Parameters: sock - The socket on which the option should be get.
* optname - The option to get.
* optval - The buffer into which the value should be copied.
* optlen - The length of the buffer pointed to by optval.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_getsockopt_ip6(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
Ip_socklen_t actual_optlen = 0;
int val = 0;
void *ptr = IP_NULL;
#ifdef IPNET_USE_MCAST_ROUTING
if (optname & IP_MRT6_BASE)
return ipnet_ip6_mcast_getsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_MCAST_ROUTING */
#ifdef IPFIREWALL
if (optname == IP_IPV6_FW)
{
if (ipfirewall == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
return (int)ipfirewall_getsockopt(sock, optname, optval, optlen);
}
#endif /* IPFIREWALL */
/* Determine length (or minimal length) of option */
switch (optname)
{
case IP_IPV6_PKTINFO:
actual_optlen = sizeof(struct Ip_in6_pktinfo);
break;
case IP_IPV6_NEXTHOP:
actual_optlen = sizeof(struct Ip_sockaddr_in6);
break;
case IP_IPV6_RTHDR:
ptr = sock->ip6->exthdr_rthdr;
break;
case IP_IPV6_HOPOPTS:
ptr = sock->ip6->exthdr_hbh;
break;
case IP_IPV6_DSTOPTS:
ptr = sock->ip6->exthdr_dst;
break;
case IP_IPV6_RTHDRDSTOPTS:
ptr = sock->ip6->exthdr_rthdrdst;
break;
default:
actual_optlen = sizeof(int);
break;
}
if (ptr != IP_NULL)
actual_optlen = (((Ip_pkt_ip6_ext_hdr *) ptr)->len + 1) * 8;
if (*optlen < actual_optlen)
{
*optlen = actual_optlen;
return -IP_ERRNO_ENOBUFS;
}
*optlen = actual_optlen;
/* Copy the option value into the user buffer */
switch (optname)
{
case IP_IPV6_V6ONLY:
val = (int) IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_V6ONLY);
break;
case IP_IPV6_CHECKSUM:
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM))
val = sock->chk_offset;
else
val = -1;
break;
case IP_IPV6_UNICAST_HOPS:
val = sock->uni_hop_limit;
break;
case IP_IPV6_MULTICAST_HOPS:
val = sock->multi_hop_limit;
break;
case IP_IPV6_RECVIF:
val = IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
break;
case IP_IPV6_X_SENDERLINKADDR:
val = IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
break;
case IP_IPV6_X_UDPENCAP:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVENCAP);
break;
case IP_IPV6_RECVPKTINFO:
val = IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
break;
case IP_IPV6_RECVHOPLIMIT:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPLIMIT);
break;
case IP_IPV6_RECVTCLASS:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVTCLASS);
break;
case IP_IPV6_RECVRTHDR:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVRTHDR);
break;
case IP_IPV6_RECVHOPOPTS:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPOPTS);
break;
case IP_IPV6_RECVDSTOPTS:
val = IP_BIT_ISTRUE(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVDSTOPTS);
break;
case IP_IPV6_MULTICAST_LOOP:
val = IP_BIT_ISTRUE(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
break;
case IP_IPV6_ADDR_PREFERENCES:
val = sock->ip6->prefer_src_flags;
break;
case IP_IPV6_MULTICAST_IF:
val = (int)sock->ip6->multicast_if;
break;
case IP_IPV6_TCLASS:
val = sock->ip6->tclass;
break;
case IP_IPV6_PKTINFO:
ptr = sock->ip6->pktinfo;
if (ptr == IP_NULL)
{
/* Must return a IPV6_PKTINFO option with ifindex == 0 and
the unspecified address if no set */
ipcom_memset(optval, 0, actual_optlen);
return 0;
}
break;
case IP_IPV6_NEXTHOP:
ptr = sock->ip6->nexthop;
break;
case IP_IPV6_RTHDR:
case IP_IPV6_HOPOPTS:
case IP_IPV6_DSTOPTS:
case IP_IPV6_RTHDRDSTOPTS:
/* 'ptr' is already set */
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
if (actual_optlen > 0)
{
if (ptr != IP_NULL)
ipcom_memcpy(optval, ptr, actual_optlen);
else
*(int*) optval = val;
}
return 0;
}
/*
*===========================================================================
* ipnet_setsockopt_ip6_update_sticky_option_flag
*===========================================================================
* Description: Sets the sticky option flag if sticky option(s) is
* present, clears the flag otherwise.
* Parameters: sock - The socket on which the option should be set.
* optname - The option to set.
* optval - The value to set.
* optlen - The length of the buffer pointed to by optval.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC void
ipnet_setsockopt_ip6_update_sticky_option_flag(Ipnet_socket *sock)
{
if (sock->ip6->pktinfo != IP_NULL
|| sock->ip6->nexthop != IP_NULL
|| sock->ip6->tclass != 0
|| sock->ip6->exthdr_hbh != IP_NULL
|| sock->ip6->exthdr_dst != IP_NULL
|| sock->ip6->exthdr_rthdr != IP_NULL
|| sock->ip6->exthdr_rthdrdst != IP_NULL
|| sock->ip6->prefer_src_flags != 0)
{
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_HAS_STICKY_OPT);
}
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_HAS_STICKY_OPT);
}
/*
*===========================================================================
* ipnet_setsockopt_ip6
*===========================================================================
* Description: Set socket option where level was set to IP_IPPROTO_IPV6.
* Parameters: sock - The socket on which the option should be set.
* optname - The option to set.
* optval - The value to set.
* optlen - The length of the buffer pointed to by optval.
* Returns: >=0 = success, <0 = error code.
*
*/
/*lint -efunc(429,ipnet_setsockopt_ip6) Custodial pointer 'ptr' (line X) has not been freed or returned*/
IP_STATIC int
ipnet_setsockopt_ip6(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
struct Ip_ipv6_mreq *mreq = (void *)optval;
struct Ip_group_source_req *gsreq = (void *)optval;
int val = 0;
void *ptr = IP_NULL;
void *old_ptr = IP_NULL;
Ipnet_netif *netif;
Ip_socklen_t actual_optlen;
Ip_bool is_minimal_len = IP_FALSE;
struct Ip_sockaddr_in6 *group;
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
struct Ip_sockaddr_in6 *source;
#endif
#ifdef IPNET_USE_MCAST_ROUTING
if (optname & IP_MRT6_BASE)
return ipnet_ip6_mcast_setsockopt(sock, optname, optval, optlen);
#endif /* IPNET_USE_MCAST_ROUTING */
#ifdef IPFIREWALL
if (optname == IP_IPV6_FW)
{
if (ipfirewall == IP_NULL)
return -IP_ERRNO_EOPNOTSUPP;
return (int)ipfirewall_setsockopt(sock, optname, optval, optlen);
}
#endif /* IPFIREWALL */
switch (optname)
{
case IP_IPV6_PKTINFO:
case IP_IPV6_NEXTHOP:
case IP_IPV6_RTHDR:
case IP_IPV6_HOPOPTS:
case IP_IPV6_DSTOPTS:
case IP_IPV6_RTHDRDSTOPTS:
if (optlen == 0)
/* Remove sticky option */
goto remove_sticky_option;
break;
default:
break;
}
/* Set the actual length of the option */
switch (optname)
{
case IP_IPV6_PKTINFO:
actual_optlen = sizeof(struct Ip_in6_pktinfo);
break;
case IP_IPV6_NEXTHOP:
actual_optlen = sizeof(struct Ip_sockaddr_in6);
is_minimal_len = IP_TRUE;
break;
case IP_IPV6_RTHDR:
actual_optlen = sizeof(struct Ip_ip6_rthdr);
is_minimal_len = IP_TRUE;
break;
case IP_IPV6_HOPOPTS:
actual_optlen = sizeof(struct Ip_ip6_hbh);
is_minimal_len = IP_TRUE;
break;
case IP_IPV6_DSTOPTS:
case IP_IPV6_RTHDRDSTOPTS:
actual_optlen = sizeof(struct Ip_ip6_dest);
is_minimal_len = IP_TRUE;
break;
case IP_IPV6_ADD_MEMBERSHIP:
case IP_IPV6_DROP_MEMBERSHIP:
actual_optlen = sizeof(struct Ip_ipv6_mreq);
break;
case IP_IPV6_JOIN_ANYCAST:
case IP_IPV6_LEAVE_ANYCAST:
actual_optlen = sizeof(struct Ip_ipv6_mreq);
break;
case IP_MCAST_JOIN_GROUP:
case IP_MCAST_LEAVE_GROUP:
actual_optlen = sizeof(struct Ip_group_req);
break;
case IP_MCAST_BLOCK_SOURCE:
case IP_MCAST_UNBLOCK_SOURCE:
case IP_MCAST_JOIN_SOURCE_GROUP:
case IP_MCAST_LEAVE_SOURCE_GROUP:
actual_optlen = sizeof(struct Ip_group_source_req);
break;
default:
actual_optlen = sizeof(int);
break;
}
/* Check that the options has the correct length */
if ((is_minimal_len && optlen < actual_optlen)
|| (is_minimal_len == IP_FALSE && optlen != actual_optlen))
{
return -IP_ERRNO_EINVAL;
}
remove_sticky_option:
/* Copy the value to a local buffer */
switch (optname)
{
case IP_IPV6_RTHDR:
case IP_IPV6_PKTINFO: /*lint !e616 control flows into case/default */
case IP_IPV6_NEXTHOP:
case IP_IPV6_HOPOPTS:
case IP_IPV6_DSTOPTS:
case IP_IPV6_RTHDRDSTOPTS:
if (optlen == 0)
ptr = IP_NULL;
else
{
ptr = ipcom_malloc(optlen);
if (ptr == IP_NULL)
return -IP_ERRNO_ENOMEM;
ipcom_memcpy(ptr, optval, optlen);
}
break;
default:
val = *(int*) optval;
break;
}
/* Set the value */
switch (optname)
{
case IP_IPV6_V6ONLY:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_V6ONLY);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_V6ONLY);
break;
case IP_IPV6_CHECKSUM:
if (sock->proto == IP_IPPROTO_TCP
|| sock->proto == IP_IPPROTO_UDP
|| sock->proto == IP_IPPROTO_UDPLITE
|| sock->proto == IP_IPPROTO_ICMPV6)
{
/* Can't change the checksum offset for those protocols. */
return -IP_ERRNO_EACCES;
}
if (val == -1)
{
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);
sock->chk_offset = 0;
}
else
{
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);
sock->chk_offset = (Ip_u16) val;
}
break;
case IP_IPV6_RECVIF:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_RECV_IF);
break;
case IP_IPV6_X_UDPENCAP:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVENCAP);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVENCAP);
break;
case IP_IPV6_X_SENDERLINKADDR:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_SENDER_LINK_ADDR);
break;
case IP_IPV6_RECVPKTINFO:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_RECVPKTINFO);
break;
case IP_IPV6_RECVHOPLIMIT:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPLIMIT);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPLIMIT);
break;
case IP_IPV6_RECVTCLASS:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVTCLASS);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVTCLASS);
break;
case IP_IPV6_RECVRTHDR:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVRTHDR);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVRTHDR);
break;
case IP_IPV6_RECVHOPOPTS:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPOPTS);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVHOPOPTS);
break;
case IP_IPV6_RECVDSTOPTS:
if (val)
IP_BIT_SET(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVDSTOPTS);
else
IP_BIT_CLR(sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_RECVDSTOPTS);
break;
case IP_IPV6_UNICAST_HOPS:
if (val == -1)
sock->uni_hop_limit = IPNET_IP6_OUTPUT_PARAM_USE_DEFAULT_HLIM;
else if (val >= 0 && val <= 255)
sock->uni_hop_limit = (Ip_u8) val;
else
return -IP_ERRNO_EINVAL;
break;
case IP_IPV6_MULTICAST_HOPS:
if (val == -1)
sock->multi_hop_limit = 1;
else if (val >= 0 && val <= 255)
sock->multi_hop_limit = (Ip_u8)val;
else
return -IP_ERRNO_EINVAL;
break;
case IP_IPV6_MULTICAST_LOOP:
if (val)
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
else
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_LOOP_MULTICAST);
break;
case IP_IPV6_MULTICAST_IF:
if (val < 0)
return -IP_ERRNO_EINVAL;
if (val == 0)
{
/* Let the stack choose again */
sock->ip6->multicast_if = 0;
return 0;
}
if (ipnet_if_indextonetif(sock->vr_index, (Ip_u32) val) == IP_NULL)
return -IP_ERRNO_ENXIO;
sock->ip6->multicast_if = (Ip_u32)val;
return 0;
case IP_IPV6_JOIN_ANYCAST:
case IP_IPV6_LEAVE_ANYCAST:
{
Ipnet_ip6_addr_entry *addr;
netif = ipnet_if_indextonetif(sock->vr_index, mreq->ipv6mr_interface);
if (netif == IP_NULL)
return -IP_ERRNO_ENXIO;
addr = ipnet_ip6_get_addr_entry(&mreq->ipv6mr_acaddr, netif->vr_index, netif);
/* Sanity */
if (addr && addr->type != IPNET_ADDR_TYPE_ANYCAST)
return -IP_ERRNO_EINVAL;
/* Join or leave */
if (optname == IP_IPV6_JOIN_ANYCAST)
{
return ipnet_ip6_add_addr(netif,
&mreq->ipv6mr_acaddr,
IPNET_IP6_ADDR_FLAG_DONT_ADD_NETWORK_RT | IPNET_IP6_ADDR_FLAG_ANYCAST,
IPCOM_ADDR_INFINITE,
IPCOM_ADDR_INFINITE,
128);
}
else
{
return ipnet_ip6_remove_addr(netif,
&mreq->ipv6mr_acaddr);
}
}
case IP_IPV6_JOIN_GROUP:
case IP_IPV6_LEAVE_GROUP:
{
struct Ip_group_req gr_req;
ipcom_memset(&gr_req, 0, sizeof(gr_req));
gr_req.gr_interface = mreq->ipv6mr_interface;
group = (struct Ip_sockaddr_in6 *) &gr_req.gr_group;
IPCOM_SA_LEN_SET(group, sizeof(gr_req.gr_group));
group->sin6_family = IP_AF_INET6;
group->sin6_addr = mreq->ipv6mr_multiaddr;
return ipnet_setsockopt_ip6(sock,
optname == IP_IPV6_JOIN_GROUP ? IP_MCAST_JOIN_GROUP : IP_MCAST_LEAVE_GROUP,
&gr_req,
sizeof(gr_req));
}
case IP_MCAST_JOIN_GROUP:
case IP_MCAST_LEAVE_GROUP:
gsreq = (struct Ip_group_source_req *) optval;
if (optlen < sizeof(struct Ip_group_req)
|| gsreq->gsr_group.ss_family != IP_AF_INET6
|| IPCOM_SA_LEN_GET(&gsreq->gsr_group) < sizeof(struct Ip_sockaddr_in6))
return -IP_ERRNO_EINVAL;
netif = ipnet_if_indextonetif(sock->vr_index, gsreq->gsr_interface);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
group = (struct Ip_sockaddr_in6 *) &gsreq->gsr_group;
if (IP_IN6_IS_ADDR_UNSPECIFIED(&group->sin6_addr))
return ipnet_sockopt_enter_exit_promisc(sock, netif, optname == IP_MCAST_JOIN_GROUP);
if (optname == IP_MCAST_LEAVE_GROUP)
return ipnet_sock_ip6_mcast_delete_data(sock, netif, &group->sin6_addr);
return ipnet_sockopt_ip6_mcast_create_data(sock, netif, &group->sin6_addr);
case IP_MCAST_BLOCK_SOURCE:
case IP_MCAST_UNBLOCK_SOURCE:
case IP_MCAST_JOIN_SOURCE_GROUP:
case IP_MCAST_LEAVE_SOURCE_GROUP:
#ifdef IPNET_USE_SOURCE_SPECIFIC_MCAST
gsreq = (struct Ip_group_source_req *) optval;
if (optlen < sizeof(struct Ip_group_source_req)
|| gsreq->gsr_group.ss_family != IP_AF_INET6
|| IPCOM_SA_LEN_GET(&gsreq->gsr_group) < sizeof(struct Ip_sockaddr_in6)
|| gsreq->gsr_source.ss_family != IP_AF_INET6
|| IPCOM_SA_LEN_GET(&gsreq->gsr_source) < sizeof(struct Ip_sockaddr_in6))
return -IP_ERRNO_EINVAL;
netif = ipnet_if_indextonetif(sock->vr_index, gsreq->gsr_interface);
if (netif == IP_NULL)
return -IP_ERRNO_EADDRNOTAVAIL;
group = (struct Ip_sockaddr_in6 *) &gsreq->gsr_group;
source = (struct Ip_sockaddr_in6 *) &gsreq->gsr_source;
return ipnet_sockopt_ip6_mcast_add_remove_source(sock,
netif,
&group->sin6_addr,
&source->sin6_addr,
(optname == IP_MCAST_BLOCK_SOURCE
|| optname == IP_MCAST_UNBLOCK_SOURCE
? IPCOM_SET_IS_EXCLUDE
: IPCOM_SET_IS_INCLUDE),
(optname == IP_MCAST_BLOCK_SOURCE
|| optname == IP_MCAST_JOIN_SOURCE_GROUP));
#else
return -IP_ERRNO_EOPNOTSUPP;
#endif /* IPNET_USE_SOURCE_SPECIFIC_MCAST */
case IP_IPV6_TCLASS:
if (val == -1)
sock->ip6->tclass = 0;
else if (val >= 0 && val <= 255)
sock->ip6->tclass = (Ip_u8)val;
else
return -IP_ERRNO_EINVAL;
break;
case IP_IPV6_PKTINFO:
if (ptr != IP_NULL
&& sock->proto == IP_IPPROTO_TCP
&& IP_IN6_IS_ADDR_UNSPECIFIED(&((struct Ip_in6_pktinfo *) ptr)->ipi6_addr) == IP_FALSE)
/* It is not allowed to change the source address for TCP */
{
ipcom_free(ptr);
return -IP_ERRNO_EINVAL;
}
old_ptr = sock->ip6->pktinfo;
sock->ip6->pktinfo = ptr;
if (ptr != IP_NULL
&& sock->ip6->pktinfo->ipi6_ifindex == 0
&& IP_IN6_IS_ADDR_UNSPECIFIED(&sock->ip6->pktinfo->ipi6_addr))
{
/* Set the default value */
sock->ip6->pktinfo = IP_NULL;
ipcom_free(ptr);
}
break;
case IP_IPV6_NEXTHOP:
old_ptr = sock->ip6->nexthop;
sock->ip6->nexthop = ptr;
break;
case IP_IPV6_RTHDR:
/* Only allow rthdr type 0 as sticky */
if (ptr != IP_NULL
&& ((Ip_pkt_ip6_rthdr *) ptr)->ip6r_type != IP_IPV6_RTHDR_TYPE_0)
{
ipcom_free(ptr);
return -IP_ERRNO_EINVAL;
}
old_ptr = sock->ip6->exthdr_rthdr;
sock->ip6->exthdr_rthdr = ptr;
break;
case IP_IPV6_HOPOPTS:
old_ptr = sock->ip6->exthdr_hbh;
sock->ip6->exthdr_hbh = ptr;
break;
case IP_IPV6_DSTOPTS:
old_ptr = sock->ip6->exthdr_dst;
sock->ip6->exthdr_dst = ptr;
break;
case IP_IPV6_RTHDRDSTOPTS:
old_ptr = sock->ip6->exthdr_rthdrdst;
sock->ip6->exthdr_rthdrdst = ptr;
break;
case IP_IPV6_ADDR_PREFERENCES:
sock->ip6->prefer_src_flags = (Ip_u8) val;
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
ipnet_setsockopt_ip6_update_sticky_option_flag(sock);
ipcom_free(old_ptr);
return 0;
}
/*
*===========================================================================
* ipnet_getsockopt_icmp6
*===========================================================================
* Description: Get socket option where level was set to IP_IPPROTO_ICMPV6.
* Parameters: sock - The socket on which the option should be get.
* optname - The option to get.
* optval - The buffer into which the value should be copied.
* optlen - The length of the buffer pointed to by optval.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_getsockopt_icmp6(Ipnet_socket *sock, int optname, void *optval, Ip_socklen_t *optlen)
{
switch (optname)
{
case IP_ICMP6_FILTER:
if (*optlen < sizeof(struct Ip_icmp6_filter))
return -IP_ERRNO_EINVAL;
*optlen = sizeof(struct Ip_icmp6_filter);
ipcom_memcpy(optval, sock->ip6->icmp6_filter, sizeof(struct Ip_icmp6_filter));
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
return 0;
}
/*
*===========================================================================
* ipnet_setsockopt_icmp6
*===========================================================================
* Description: Set socket option where level was set to IP_IPPROTO_ICMPV6.
* Parameters: sock - The socket on which the option should be set.
* optname - The option to set.
* optval - The value to set.
* optlen - The length of the buffer pointed to by optval.
* Returns: >=0 = success, <0 = error code.
*
*/
IP_STATIC int
ipnet_setsockopt_icmp6(Ipnet_socket *sock, int optname, IP_CONST void *optval, Ip_socklen_t optlen)
{
switch (optname)
{
case IP_ICMP6_FILTER:
if (optlen != sizeof(struct Ip_icmp6_filter))
return -IP_ERRNO_EINVAL;
ipcom_memcpy(sock->ip6->icmp6_filter, optval, optlen);
break;
default:
return -IP_ERRNO_EOPNOTSUPP;
}
return 0;
}
#endif /* IPCOM_USE_INET6 */
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sockopt_ip4_addr_to_netif
*===========================================================================
* Description: Returns the interface that has the specified address.
* Parameters: vr - The virtual router the returned interface must be
* assigned to.
* addr - The address the interface must have.
* Returns: Pointer to the interface structure or IP_NULL if no interface
* has the specified address.
*
*/
#ifdef IPCOM_USE_INET
IP_GLOBAL Ipnet_netif *
ipnet_sockopt_ip4_addr_to_netif(Ip_u16 vr, struct Ip_in_addr *in_addr)
{
Ipnet_ip4_addr_entry *addr;
if (in_addr->s_addr == IP_INADDR_ANY)
{
/* Return the default multicast interface in this case */
struct Ip_in_addr mcast_addr;
Ipnet_route_entry *rt;
mcast_addr.s_addr = ip_htonl(IP_INADDR_UNSPEC_GROUP);
if (ipnet_route_lookup(IP_AF_INET, vr, IPCOM_ROUTE_TABLE_DEFAULT, 0, &mcast_addr, 0, 0, &rt) >= 0)
return rt->netif;
}
addr = ipnet_ip4_get_addr_entry(in_addr->s_addr, vr, IP_NULL);
return addr != IP_NULL ? ipnet_ip4_addr_to_netif(addr) : IP_NULL;
}
#endif /* IPCOM_USE_INET */
/*
*===========================================================================
* ipnet_sockopt_release_sticky_ip6opts
*===========================================================================
* Description: Frees all sticky IPv6 options assigned to a socket
* Parameters: sock - an IPv6 socket.
* Returns:
*
*/
#ifdef IPCOM_USE_INET6
IP_GLOBAL void
ipnet_sockopt_release_sticky_ip6opts(Ipnet_socket *sock)
{
static const int sticky_opts[] = {
IP_IPV6_PKTINFO,
IP_IPV6_NEXTHOP,
IP_IPV6_RTHDR,
IP_IPV6_HOPOPTS,
IP_IPV6_DSTOPTS,
IP_IPV6_RTHDRDSTOPTS
};
int i;
for (i = 0; i < sizeof(sticky_opts) / sizeof(*sticky_opts); i++)
(void)ipnet_setsockopt_ip6(sock, sticky_opts[i], IP_NULL, 0);
}
#endif /* IPCOM_USE_INET6 */
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sys_getsockopt
*===========================================================================
* Description: Gets the specified option on a socket. Options may
* exist at multiple protocol levels.
* Parameters: fd - A socket descriptor.
* level - The protocol level that should be affected by the
* option.
* optname - The option name.
* optval - Pointer to a buffer where the option data should be
* copied..
* optlen - The length of the buffer poited to by optval.
* Returns: 0 = success, -1 = error.
*
*/
IP_GLOBAL int
ipnet_sys_getsockopt(Ip_fd fd,
int level,
int optname,
void *optval,
Ip_socklen_t *optlen)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
int err = -IP_ERRNO_EINVAL;
ip_assert2(optval != IP_NULL);
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_SOCKET(fd, sock);
ops = ipnet_sock_get_ops(sock);
if (ops->getopt != IP_NULL)
{
err = ops->getopt(sock, level, optname, optval, optlen);
if (err != -IP_ERRNO_EOPNOTSUPP)
return err;
/* else: try the IPNET socket options handlers */
}
switch (level)
{
case IP_SOL_SOCKET:
err = ipnet_getsockopt_sol(sock, optname, optval, optlen);
break;
case IP_IPPROTO_UDP:
case IP_IPPROTO_UDPLITE:
if (level != sock->proto)
return -IP_ERRNO_EINVAL;
err = ipnet_getsockopt_udp(sock, optname, optval, optlen);
break;
case IP_SOL_RAW:
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET && sock->proto == IP_IPPROTO_ICMP)
err = ipnet_getsockopt_icmp(sock, optname, optval, optlen);
#endif /* IPCOM_USE_INET */
break;
#ifdef IPCOM_USE_INET
case IP_IPPROTO_IP:
if (sock->ipcom.domain == IP_AF_INET)
err = ipnet_getsockopt_ip4(sock, optname, optval, optlen);
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_IPPROTO_IPV6:
if (sock->ipcom.domain == IP_AF_INET6)
err = ipnet_getsockopt_ip6(sock, optname, optval, optlen);
break;
case IP_IPPROTO_ICMPV6:
if (sock->ipcom.domain == IP_AF_INET6 && sock->proto == IP_IPPROTO_ICMPV6)
err = ipnet_getsockopt_icmp6(sock, optname, optval, optlen);
break;
#endif
#ifdef IPCOM_USE_PACKET
case IP_SOL_PACKET:
err = ipnet_packet_getsockopt(optname, optval, optlen);
break;
#endif /* IPCOM_USE_PACKET */
default:
err = -IP_ERRNO_EOPNOTSUPP;
break;
}
return err;
}
/*
*===========================================================================
* ipnet_sys_setsockopt
*===========================================================================
* Description: Sets the specified option on a socket. Options may
* exist at multiple protocol levels.
* Parameters: fd - A socket descriptor.
* level - The protocol level that should be affected by the
* option.
* optname - The option name.
* optval - Data to set for the option.
* optlen - The length of the buffer poited to by optval.
* Returns: 0 = success, -1 = error.
*
*/
IP_GLOBAL int
ipnet_sys_setsockopt(Ip_fd fd,
int level,
int optname,
IP_CONST void *optval,
Ip_socklen_t optlen)
{
Ipnet_socket *sock;
Ipnet_sock_ops *ops;
int err = -IP_ERRNO_EINVAL;
ip_assert2(optval != IP_NULL);
ip_assert2(fd >= IPNET_SOCKET_STARTFD);
ip_assert2(fd < ipnet_conf_max_sockets);
ip_assert2(ipnet->sockets[fd] != IP_NULL);
IPNET_FD_TO_SOCKET(fd, sock);
ipnet_sock_make_next_sendmsg_synchronous(sock);
ops = ipnet_sock_get_ops(sock);
if (ops->setopt != IP_NULL)
{
err = ops->setopt(sock, level, optname, optval, optlen);
if (err != -IP_ERRNO_EOPNOTSUPP)
return err;
/* else: try the IPNET socket options handlers */
}
switch (level)
{
case IP_SOL_SOCKET:
err = ipnet_setsockopt_sol(sock, optname, optval, optlen);
break;
case IP_IPPROTO_UDP:
case IP_IPPROTO_UDPLITE:
if (level != sock->proto)
return -IP_ERRNO_EINVAL;
err = ipnet_setsockopt_udp(sock, optname, optval, optlen);
break;
case IP_SOL_RAW:
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET && sock->proto == IP_IPPROTO_ICMP)
err = ipnet_setsockopt_icmp(sock, optname, optval, optlen);
#endif /* IPCOM_USE_INET */
break;
#ifdef IPCOM_USE_INET
case IP_IPPROTO_IP:
if (sock->ipcom.domain == IP_AF_INET)
err = ipnet_setsockopt_ip4(sock, optname, optval, optlen);
break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
case IP_IPPROTO_IPV6:
if (sock->ipcom.domain == IP_AF_INET6)
err = ipnet_setsockopt_ip6(sock, optname, optval, optlen);
break;
case IP_IPPROTO_ICMPV6:
if (sock->ipcom.domain == IP_AF_INET6 && sock->proto == IP_IPPROTO_ICMPV6)
err = ipnet_setsockopt_icmp6(sock, optname, optval, optlen);
break;
#endif /* IPCOM_USE_INET6 */
#ifdef IPCOM_USE_PACKET
case IP_SOL_PACKET:
err = ipnet_packet_setsockopt(sock, optname, optval, optlen);
break;
#endif /* IPCOM_USE_PACKET */
default:
err = -IP_ERRNO_EOPNOTSUPP;
break;
}
return err;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/