1*ef8d499eSDavid van Moolenbroek /* LWIP service - ipsock.c - shared IP-level socket code */
2*ef8d499eSDavid van Moolenbroek
3*ef8d499eSDavid van Moolenbroek #include "lwip.h"
4*ef8d499eSDavid van Moolenbroek #include "ifaddr.h"
5*ef8d499eSDavid van Moolenbroek
6*ef8d499eSDavid van Moolenbroek #define ip6_hdr __netbsd_ip6_hdr /* conflicting definitions */
7*ef8d499eSDavid van Moolenbroek #include <net/route.h>
8*ef8d499eSDavid van Moolenbroek #include <netinet/ip.h>
9*ef8d499eSDavid van Moolenbroek #include <netinet/in_pcb.h>
10*ef8d499eSDavid van Moolenbroek #include <netinet6/in6_pcb.h>
11*ef8d499eSDavid van Moolenbroek #undef ip6_hdr
12*ef8d499eSDavid van Moolenbroek
13*ef8d499eSDavid van Moolenbroek /* The following are sysctl(7) settings. */
14*ef8d499eSDavid van Moolenbroek int lwip_ip4_forward = 0; /* We patch lwIP to check these.. */
15*ef8d499eSDavid van Moolenbroek int lwip_ip6_forward = 0; /* ..two settings at run time. */
16*ef8d499eSDavid van Moolenbroek static int ipsock_v6only = 1;
17*ef8d499eSDavid van Moolenbroek
18*ef8d499eSDavid van Moolenbroek /* The CTL_NET PF_INET IPPROTO_IP subtree. */
19*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_ip_table[] = {
20*ef8d499eSDavid van Moolenbroek /* 1*/ [IPCTL_FORWARDING] = RMIB_INTPTR(RMIB_RW, &lwip_ip4_forward,
21*ef8d499eSDavid van Moolenbroek "forwarding",
22*ef8d499eSDavid van Moolenbroek "Enable forwarding of INET diagrams"),
23*ef8d499eSDavid van Moolenbroek /* 3*/ [IPCTL_DEFTTL] = RMIB_INT(RMIB_RO, IP_DEFAULT_TTL, "ttl",
24*ef8d499eSDavid van Moolenbroek "Default TTL for an INET diagram"),
25*ef8d499eSDavid van Moolenbroek /*23*/ [IPCTL_LOOPBACKCKSUM] = RMIB_FUNC(RMIB_RW | CTLTYPE_INT, sizeof(int),
26*ef8d499eSDavid van Moolenbroek loopif_cksum, "do_loopback_cksum",
27*ef8d499eSDavid van Moolenbroek "Perform IP checksum on loopback"),
28*ef8d499eSDavid van Moolenbroek };
29*ef8d499eSDavid van Moolenbroek
30*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet_ip_node =
31*ef8d499eSDavid van Moolenbroek RMIB_NODE(RMIB_RO, net_inet_ip_table, "ip", "IPv4 related settings");
32*ef8d499eSDavid van Moolenbroek
33*ef8d499eSDavid van Moolenbroek /* The CTL_NET PF_INET6 IPPROTO_IPV6 subtree. */
34*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet6_ip6_table[] = {
35*ef8d499eSDavid van Moolenbroek /* 1*/ [IPV6CTL_FORWARDING] = RMIB_INTPTR(RMIB_RW, &lwip_ip6_forward,
36*ef8d499eSDavid van Moolenbroek "forwarding",
37*ef8d499eSDavid van Moolenbroek "Enable forwarding of INET6 diagrams"),
38*ef8d499eSDavid van Moolenbroek /*
39*ef8d499eSDavid van Moolenbroek * The following functionality is not
40*ef8d499eSDavid van Moolenbroek * implemented in lwIP at this time.
41*ef8d499eSDavid van Moolenbroek */
42*ef8d499eSDavid van Moolenbroek /* 2*/ [IPV6CTL_SENDREDIRECTS] = RMIB_INT(RMIB_RO, 0, "redirect", "Enable "
43*ef8d499eSDavid van Moolenbroek "sending of ICMPv6 redirect messages"),
44*ef8d499eSDavid van Moolenbroek /* 3*/ [IPV6CTL_DEFHLIM] = RMIB_INT(RMIB_RO, IP_DEFAULT_TTL, "hlim",
45*ef8d499eSDavid van Moolenbroek "Hop limit for an INET6 datagram"),
46*ef8d499eSDavid van Moolenbroek /*12*/ [IPV6CTL_ACCEPT_RTADV] = RMIB_INTPTR(RMIB_RW, &ifaddr_accept_rtadv,
47*ef8d499eSDavid van Moolenbroek "accept_rtadv",
48*ef8d499eSDavid van Moolenbroek "Accept router advertisements"),
49*ef8d499eSDavid van Moolenbroek /*16*/ [IPV6CTL_DAD_COUNT] = RMIB_INT(RMIB_RO,
50*ef8d499eSDavid van Moolenbroek LWIP_IPV6_DUP_DETECT_ATTEMPTS, "dad_count",
51*ef8d499eSDavid van Moolenbroek "Number of Duplicate Address Detection "
52*ef8d499eSDavid van Moolenbroek "probes to send"),
53*ef8d499eSDavid van Moolenbroek /*24*/ [IPV6CTL_V6ONLY] = RMIB_INTPTR(RMIB_RW, &ipsock_v6only,
54*ef8d499eSDavid van Moolenbroek "v6only", "Disallow PF_INET6 sockets from "
55*ef8d499eSDavid van Moolenbroek "connecting to PF_INET sockets"),
56*ef8d499eSDavid van Moolenbroek /*
57*ef8d499eSDavid van Moolenbroek * The following setting is significantly
58*ef8d499eSDavid van Moolenbroek * different from NetBSD, and therefore it has
59*ef8d499eSDavid van Moolenbroek * a somewhat different description as well.
60*ef8d499eSDavid van Moolenbroek */
61*ef8d499eSDavid van Moolenbroek /*35*/ [IPV6CTL_AUTO_LINKLOCAL]= RMIB_INTPTR(RMIB_RW, &ifaddr_auto_linklocal,
62*ef8d499eSDavid van Moolenbroek "auto_linklocal", "Enable global support "
63*ef8d499eSDavid van Moolenbroek "for adding IPv6link-local addresses to "
64*ef8d499eSDavid van Moolenbroek "interfaces"),
65*ef8d499eSDavid van Moolenbroek /*
66*ef8d499eSDavid van Moolenbroek * Temporary addresses are managed entirely by
67*ef8d499eSDavid van Moolenbroek * userland. We only maintain the settings.
68*ef8d499eSDavid van Moolenbroek */
69*ef8d499eSDavid van Moolenbroek /*+0*/ [IPV6CTL_MAXID] = RMIB_INT(RMIB_RW, 0, "use_tempaddr",
70*ef8d499eSDavid van Moolenbroek "Use temporary address"),
71*ef8d499eSDavid van Moolenbroek /*+1*/ [IPV6CTL_MAXID + 1] = RMIB_INT(RMIB_RW, 86400, "temppltime",
72*ef8d499eSDavid van Moolenbroek "Preferred lifetime of a temporary "
73*ef8d499eSDavid van Moolenbroek "address"),
74*ef8d499eSDavid van Moolenbroek /*+2*/ [IPV6CTL_MAXID + 2] = RMIB_INT(RMIB_RW, 604800, "tempvltime",
75*ef8d499eSDavid van Moolenbroek "Valid lifetime of a temporary address"),
76*ef8d499eSDavid van Moolenbroek };
77*ef8d499eSDavid van Moolenbroek
78*ef8d499eSDavid van Moolenbroek static struct rmib_node net_inet6_ip6_node =
79*ef8d499eSDavid van Moolenbroek RMIB_NODE(RMIB_RO, net_inet6_ip6_table, "ip6", "IPv6 related settings");
80*ef8d499eSDavid van Moolenbroek
81*ef8d499eSDavid van Moolenbroek /*
82*ef8d499eSDavid van Moolenbroek * Initialize the IP sockets module.
83*ef8d499eSDavid van Moolenbroek */
84*ef8d499eSDavid van Moolenbroek void
ipsock_init(void)85*ef8d499eSDavid van Moolenbroek ipsock_init(void)
86*ef8d499eSDavid van Moolenbroek {
87*ef8d499eSDavid van Moolenbroek
88*ef8d499eSDavid van Moolenbroek /*
89*ef8d499eSDavid van Moolenbroek * Register the net.inet.ip and net.inet6.ip6 subtrees. Unlike for the
90*ef8d499eSDavid van Moolenbroek * specific protocols (TCP/UDP/RAW), here the IPv4 and IPv6 subtrees
91*ef8d499eSDavid van Moolenbroek * are and must be separate, even though many settings are shared
92*ef8d499eSDavid van Moolenbroek * between the two at the lwIP level. Ultimately we may have to split
93*ef8d499eSDavid van Moolenbroek * the subtrees for the specific protocols, too, though..
94*ef8d499eSDavid van Moolenbroek */
95*ef8d499eSDavid van Moolenbroek mibtree_register_inet(AF_INET, IPPROTO_IP, &net_inet_ip_node);
96*ef8d499eSDavid van Moolenbroek mibtree_register_inet(AF_INET6, IPPROTO_IPV6, &net_inet6_ip6_node);
97*ef8d499eSDavid van Moolenbroek }
98*ef8d499eSDavid van Moolenbroek
99*ef8d499eSDavid van Moolenbroek /*
100*ef8d499eSDavid van Moolenbroek * Return the lwIP IP address type (IPADDR_TYPE_) for the given IP socket.
101*ef8d499eSDavid van Moolenbroek */
102*ef8d499eSDavid van Moolenbroek static int
ipsock_get_type(struct ipsock * ip)103*ef8d499eSDavid van Moolenbroek ipsock_get_type(struct ipsock * ip)
104*ef8d499eSDavid van Moolenbroek {
105*ef8d499eSDavid van Moolenbroek
106*ef8d499eSDavid van Moolenbroek if (!(ip->ip_flags & IPF_IPV6))
107*ef8d499eSDavid van Moolenbroek return IPADDR_TYPE_V4;
108*ef8d499eSDavid van Moolenbroek else if (ip->ip_flags & IPF_V6ONLY)
109*ef8d499eSDavid van Moolenbroek return IPADDR_TYPE_V6;
110*ef8d499eSDavid van Moolenbroek else
111*ef8d499eSDavid van Moolenbroek return IPADDR_TYPE_ANY;
112*ef8d499eSDavid van Moolenbroek }
113*ef8d499eSDavid van Moolenbroek
114*ef8d499eSDavid van Moolenbroek /*
115*ef8d499eSDavid van Moolenbroek * Create an IP socket, for the given (PF_/AF_) domain and initial send and
116*ef8d499eSDavid van Moolenbroek * receive buffer sizes. Return the lwIP IP address type that should be used
117*ef8d499eSDavid van Moolenbroek * to create the corresponding PCB. Return a pointer to the libsockevent
118*ef8d499eSDavid van Moolenbroek * socket in 'sockp'. This function must not allocate any resources in any
119*ef8d499eSDavid van Moolenbroek * form, as socket creation may still fail later, in which case no destruction
120*ef8d499eSDavid van Moolenbroek * function is called.
121*ef8d499eSDavid van Moolenbroek */
122*ef8d499eSDavid van Moolenbroek int
ipsock_socket(struct ipsock * ip,int domain,size_t sndbuf,size_t rcvbuf,struct sock ** sockp)123*ef8d499eSDavid van Moolenbroek ipsock_socket(struct ipsock * ip, int domain, size_t sndbuf, size_t rcvbuf,
124*ef8d499eSDavid van Moolenbroek struct sock ** sockp)
125*ef8d499eSDavid van Moolenbroek {
126*ef8d499eSDavid van Moolenbroek
127*ef8d499eSDavid van Moolenbroek ip->ip_flags = (domain == AF_INET6) ? IPF_IPV6 : 0;
128*ef8d499eSDavid van Moolenbroek
129*ef8d499eSDavid van Moolenbroek if (domain == AF_INET6 && ipsock_v6only)
130*ef8d499eSDavid van Moolenbroek ip->ip_flags |= IPF_V6ONLY;
131*ef8d499eSDavid van Moolenbroek
132*ef8d499eSDavid van Moolenbroek ip->ip_sndbuf = sndbuf;
133*ef8d499eSDavid van Moolenbroek ip->ip_rcvbuf = rcvbuf;
134*ef8d499eSDavid van Moolenbroek
135*ef8d499eSDavid van Moolenbroek /* Important: when adding settings here, also change ipsock_clone(). */
136*ef8d499eSDavid van Moolenbroek
137*ef8d499eSDavid van Moolenbroek *sockp = &ip->ip_sock;
138*ef8d499eSDavid van Moolenbroek
139*ef8d499eSDavid van Moolenbroek return ipsock_get_type(ip);
140*ef8d499eSDavid van Moolenbroek }
141*ef8d499eSDavid van Moolenbroek
142*ef8d499eSDavid van Moolenbroek /*
143*ef8d499eSDavid van Moolenbroek * Clone the given socket 'ip' into the new socket 'newip', using the socket
144*ef8d499eSDavid van Moolenbroek * identifier 'newid'. In particular, tell libsockevent about the clone and
145*ef8d499eSDavid van Moolenbroek * copy over any settings from 'ip' to 'newip' that can be inherited on a
146*ef8d499eSDavid van Moolenbroek * socket. Cloning is used for new TCP connections arriving on listening TCP
147*ef8d499eSDavid van Moolenbroek * sockets. This function must not fail.
148*ef8d499eSDavid van Moolenbroek */
149*ef8d499eSDavid van Moolenbroek void
ipsock_clone(struct ipsock * ip,struct ipsock * newip,sockid_t newid)150*ef8d499eSDavid van Moolenbroek ipsock_clone(struct ipsock * ip, struct ipsock * newip, sockid_t newid)
151*ef8d499eSDavid van Moolenbroek {
152*ef8d499eSDavid van Moolenbroek
153*ef8d499eSDavid van Moolenbroek sockevent_clone(&ip->ip_sock, &newip->ip_sock, newid);
154*ef8d499eSDavid van Moolenbroek
155*ef8d499eSDavid van Moolenbroek /* Inherit all settings from the original socket. */
156*ef8d499eSDavid van Moolenbroek newip->ip_flags = ip->ip_flags;
157*ef8d499eSDavid van Moolenbroek newip->ip_sndbuf = ip->ip_sndbuf;
158*ef8d499eSDavid van Moolenbroek newip->ip_rcvbuf = ip->ip_rcvbuf;
159*ef8d499eSDavid van Moolenbroek }
160*ef8d499eSDavid van Moolenbroek
161*ef8d499eSDavid van Moolenbroek /*
162*ef8d499eSDavid van Moolenbroek * Create an <any> address for the given socket, taking into account whether
163*ef8d499eSDavid van Moolenbroek * the socket is IPv4, IPv6, or mixed. The generated address, stored in
164*ef8d499eSDavid van Moolenbroek * 'ipaddr', will have the same type as returned from the ipsock_socket() call.
165*ef8d499eSDavid van Moolenbroek */
166*ef8d499eSDavid van Moolenbroek void
ipsock_get_any_addr(struct ipsock * ip,ip_addr_t * ipaddr)167*ef8d499eSDavid van Moolenbroek ipsock_get_any_addr(struct ipsock * ip, ip_addr_t * ipaddr)
168*ef8d499eSDavid van Moolenbroek {
169*ef8d499eSDavid van Moolenbroek
170*ef8d499eSDavid van Moolenbroek ip_addr_set_any(ipsock_is_ipv6(ip), ipaddr);
171*ef8d499eSDavid van Moolenbroek
172*ef8d499eSDavid van Moolenbroek if (ipsock_is_ipv6(ip) && !ipsock_is_v6only(ip))
173*ef8d499eSDavid van Moolenbroek IP_SET_TYPE(ipaddr, IPADDR_TYPE_ANY);
174*ef8d499eSDavid van Moolenbroek }
175*ef8d499eSDavid van Moolenbroek
176*ef8d499eSDavid van Moolenbroek /*
177*ef8d499eSDavid van Moolenbroek * Verify whether the given (properly scoped) IP address is a valid source
178*ef8d499eSDavid van Moolenbroek * address for the given IP socket. The 'allow_mcast' flag indicates whether
179*ef8d499eSDavid van Moolenbroek * the source address is allowed to be a multicast address. Return OK on
180*ef8d499eSDavid van Moolenbroek * success. If 'ifdevp' is not NULL, it is filled with either the interface
181*ef8d499eSDavid van Moolenbroek * that owns the address, or NULL if the address is (while valid) not
182*ef8d499eSDavid van Moolenbroek * associated with a particular interface. On failure, return a negative error
183*ef8d499eSDavid van Moolenbroek * code. This function must be called, in one way or another, for every source
184*ef8d499eSDavid van Moolenbroek * address used for binding or sending on a IP-layer socket.
185*ef8d499eSDavid van Moolenbroek */
186*ef8d499eSDavid van Moolenbroek int
ipsock_check_src_addr(struct ipsock * ip,ip_addr_t * ipaddr,int allow_mcast,struct ifdev ** ifdevp)187*ef8d499eSDavid van Moolenbroek ipsock_check_src_addr(struct ipsock * ip, ip_addr_t * ipaddr, int allow_mcast,
188*ef8d499eSDavid van Moolenbroek struct ifdev ** ifdevp)
189*ef8d499eSDavid van Moolenbroek {
190*ef8d499eSDavid van Moolenbroek ip6_addr_t *ip6addr;
191*ef8d499eSDavid van Moolenbroek struct ifdev *ifdev;
192*ef8d499eSDavid van Moolenbroek uint32_t inaddr, zone;
193*ef8d499eSDavid van Moolenbroek int is_mcast;
194*ef8d499eSDavid van Moolenbroek
195*ef8d499eSDavid van Moolenbroek /*
196*ef8d499eSDavid van Moolenbroek * TODO: for now, forbid binding to multicast addresses. Callers that
197*ef8d499eSDavid van Moolenbroek * never allow multicast addresses anyway (e.g., IPV6_PKTINFO) should
198*ef8d499eSDavid van Moolenbroek * do their own check for this; the one here may eventually be removed.
199*ef8d499eSDavid van Moolenbroek */
200*ef8d499eSDavid van Moolenbroek is_mcast = ip_addr_ismulticast(ipaddr);
201*ef8d499eSDavid van Moolenbroek
202*ef8d499eSDavid van Moolenbroek if (is_mcast && !allow_mcast)
203*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
204*ef8d499eSDavid van Moolenbroek
205*ef8d499eSDavid van Moolenbroek if (IP_IS_V6(ipaddr)) {
206*ef8d499eSDavid van Moolenbroek /*
207*ef8d499eSDavid van Moolenbroek * The given address must not have a KAME-style embedded zone.
208*ef8d499eSDavid van Moolenbroek * This check is already performed in addr_get_inet(), but we
209*ef8d499eSDavid van Moolenbroek * have to replicate it here because not all source addresses
210*ef8d499eSDavid van Moolenbroek * go through addr_get_inet().
211*ef8d499eSDavid van Moolenbroek */
212*ef8d499eSDavid van Moolenbroek ip6addr = ip_2_ip6(ipaddr);
213*ef8d499eSDavid van Moolenbroek
214*ef8d499eSDavid van Moolenbroek if (ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) &&
215*ef8d499eSDavid van Moolenbroek (ip6addr->addr[0] & PP_HTONL(0x0000ffffUL)))
216*ef8d499eSDavid van Moolenbroek return EINVAL;
217*ef8d499eSDavid van Moolenbroek
218*ef8d499eSDavid van Moolenbroek /*
219*ef8d499eSDavid van Moolenbroek * lwIP does not support IPv4-mapped IPv6 addresses, so these
220*ef8d499eSDavid van Moolenbroek * must be converted to plain IPv4 addresses instead. The IPv4
221*ef8d499eSDavid van Moolenbroek * 'any' address is not supported in this form. In V6ONLY
222*ef8d499eSDavid van Moolenbroek * mode, refuse connecting or sending to IPv4-mapped addresses
223*ef8d499eSDavid van Moolenbroek * at all.
224*ef8d499eSDavid van Moolenbroek */
225*ef8d499eSDavid van Moolenbroek if (ip6_addr_isipv4mappedipv6(ip6addr)) {
226*ef8d499eSDavid van Moolenbroek if (ipsock_is_v6only(ip))
227*ef8d499eSDavid van Moolenbroek return EINVAL;
228*ef8d499eSDavid van Moolenbroek
229*ef8d499eSDavid van Moolenbroek inaddr = ip6addr->addr[3];
230*ef8d499eSDavid van Moolenbroek
231*ef8d499eSDavid van Moolenbroek if (inaddr == PP_HTONL(INADDR_ANY))
232*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
233*ef8d499eSDavid van Moolenbroek
234*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(ipaddr, inaddr);
235*ef8d499eSDavid van Moolenbroek }
236*ef8d499eSDavid van Moolenbroek }
237*ef8d499eSDavid van Moolenbroek
238*ef8d499eSDavid van Moolenbroek ifdev = NULL;
239*ef8d499eSDavid van Moolenbroek
240*ef8d499eSDavid van Moolenbroek if (!ip_addr_isany(ipaddr)) {
241*ef8d499eSDavid van Moolenbroek if (IP_IS_V6(ipaddr) &&
242*ef8d499eSDavid van Moolenbroek ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN))
243*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
244*ef8d499eSDavid van Moolenbroek
245*ef8d499eSDavid van Moolenbroek /*
246*ef8d499eSDavid van Moolenbroek * If the address is a unicast address, it must be assigned to
247*ef8d499eSDavid van Moolenbroek * an interface. Otherwise, if it is a zoned multicast
248*ef8d499eSDavid van Moolenbroek * address, the zone denotes the interface. For global
249*ef8d499eSDavid van Moolenbroek * multicast addresses, we cannot determine an interface.
250*ef8d499eSDavid van Moolenbroek */
251*ef8d499eSDavid van Moolenbroek if (!is_mcast) {
252*ef8d499eSDavid van Moolenbroek if ((ifdev = ifaddr_map_by_addr(ipaddr)) == NULL)
253*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
254*ef8d499eSDavid van Moolenbroek } else {
255*ef8d499eSDavid van Moolenbroek /* Some multicast addresses are not acceptable. */
256*ef8d499eSDavid van Moolenbroek if (!addr_is_valid_multicast(ipaddr))
257*ef8d499eSDavid van Moolenbroek return EINVAL;
258*ef8d499eSDavid van Moolenbroek
259*ef8d499eSDavid van Moolenbroek if (IP_IS_V6(ipaddr) &&
260*ef8d499eSDavid van Moolenbroek ip6_addr_has_zone(ip_2_ip6(ipaddr))) {
261*ef8d499eSDavid van Moolenbroek zone = ip6_addr_zone(ip_2_ip6(ipaddr));
262*ef8d499eSDavid van Moolenbroek
263*ef8d499eSDavid van Moolenbroek if ((ifdev = ifdev_get_by_index(zone)) == NULL)
264*ef8d499eSDavid van Moolenbroek return ENXIO;
265*ef8d499eSDavid van Moolenbroek }
266*ef8d499eSDavid van Moolenbroek }
267*ef8d499eSDavid van Moolenbroek }
268*ef8d499eSDavid van Moolenbroek
269*ef8d499eSDavid van Moolenbroek if (ifdevp != NULL)
270*ef8d499eSDavid van Moolenbroek *ifdevp = ifdev;
271*ef8d499eSDavid van Moolenbroek
272*ef8d499eSDavid van Moolenbroek return OK;
273*ef8d499eSDavid van Moolenbroek }
274*ef8d499eSDavid van Moolenbroek
275*ef8d499eSDavid van Moolenbroek /*
276*ef8d499eSDavid van Moolenbroek * Retrieve and validate a source address for use in a socket bind call on
277*ef8d499eSDavid van Moolenbroek * socket 'ip'. The user-provided address is given as 'addr', with length
278*ef8d499eSDavid van Moolenbroek * 'addr_len'. The socket's current local IP address and port are given as
279*ef8d499eSDavid van Moolenbroek * 'local_ip' and 'local_port', respectively; for raw sockets, the given local
280*ef8d499eSDavid van Moolenbroek * port number is always zero. The caller's endpoint is given as 'user_endpt',
281*ef8d499eSDavid van Moolenbroek * used to make sure only root can bind to local port numbers. The boolean
282*ef8d499eSDavid van Moolenbroek * 'allow_mcast' flag indicates whether the source address is allowed to be a
283*ef8d499eSDavid van Moolenbroek * multicast address. On success, return OK with the source IP address stored
284*ef8d499eSDavid van Moolenbroek * in 'src_addr' and, if 'src_port' is not NULL, the port number to bind to
285*ef8d499eSDavid van Moolenbroek * stored in 'portp'. Otherwise, return a negative error code. This function
286*ef8d499eSDavid van Moolenbroek * performs all the tasks necessary before the socket can be bound using a lwIP
287*ef8d499eSDavid van Moolenbroek * call.
288*ef8d499eSDavid van Moolenbroek */
289*ef8d499eSDavid van Moolenbroek int
ipsock_get_src_addr(struct ipsock * ip,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt,ip_addr_t * local_ip,uint16_t local_port,int allow_mcast,ip_addr_t * src_addr,uint16_t * src_port)290*ef8d499eSDavid van Moolenbroek ipsock_get_src_addr(struct ipsock * ip, const struct sockaddr * addr,
291*ef8d499eSDavid van Moolenbroek socklen_t addr_len, endpoint_t user_endpt, ip_addr_t * local_ip,
292*ef8d499eSDavid van Moolenbroek uint16_t local_port, int allow_mcast, ip_addr_t * src_addr,
293*ef8d499eSDavid van Moolenbroek uint16_t * src_port)
294*ef8d499eSDavid van Moolenbroek {
295*ef8d499eSDavid van Moolenbroek uint16_t port;
296*ef8d499eSDavid van Moolenbroek int r;
297*ef8d499eSDavid van Moolenbroek
298*ef8d499eSDavid van Moolenbroek /*
299*ef8d499eSDavid van Moolenbroek * If the socket has been bound already, it cannot be bound again.
300*ef8d499eSDavid van Moolenbroek * We check this by checking whether the current local port is non-
301*ef8d499eSDavid van Moolenbroek * zero. This rule does not apply to raw sockets, but raw sockets have
302*ef8d499eSDavid van Moolenbroek * no port numbers anyway, so this conveniently works out. However,
303*ef8d499eSDavid van Moolenbroek * raw sockets may not be rebound after being connected, but that is
304*ef8d499eSDavid van Moolenbroek * checked before we even get here.
305*ef8d499eSDavid van Moolenbroek */
306*ef8d499eSDavid van Moolenbroek if (local_port != 0)
307*ef8d499eSDavid van Moolenbroek return EINVAL;
308*ef8d499eSDavid van Moolenbroek
309*ef8d499eSDavid van Moolenbroek /* Parse the user-provided address. */
310*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet(addr, addr_len, ipsock_get_type(ip), src_addr,
311*ef8d499eSDavid van Moolenbroek FALSE /*kame*/, &port)) != OK)
312*ef8d499eSDavid van Moolenbroek return r;
313*ef8d499eSDavid van Moolenbroek
314*ef8d499eSDavid van Moolenbroek /* Validate the user-provided address. */
315*ef8d499eSDavid van Moolenbroek if ((r = ipsock_check_src_addr(ip, src_addr, allow_mcast,
316*ef8d499eSDavid van Moolenbroek NULL /*ifdevp*/)) != OK)
317*ef8d499eSDavid van Moolenbroek return r;
318*ef8d499eSDavid van Moolenbroek
319*ef8d499eSDavid van Moolenbroek /*
320*ef8d499eSDavid van Moolenbroek * If we are interested in port numbers at all (for non-raw sockets,
321*ef8d499eSDavid van Moolenbroek * meaning portp is not NULL), make sure that only the superuser can
322*ef8d499eSDavid van Moolenbroek * bind to privileged port numbers. For raw sockets, only the
323*ef8d499eSDavid van Moolenbroek * superuser can open a socket anyway, so we need no check here.
324*ef8d499eSDavid van Moolenbroek */
325*ef8d499eSDavid van Moolenbroek if (src_port != NULL) {
326*ef8d499eSDavid van Moolenbroek if (port != 0 && port < IPPORT_RESERVED &&
327*ef8d499eSDavid van Moolenbroek !util_is_root(user_endpt))
328*ef8d499eSDavid van Moolenbroek return EACCES;
329*ef8d499eSDavid van Moolenbroek
330*ef8d499eSDavid van Moolenbroek *src_port = port;
331*ef8d499eSDavid van Moolenbroek }
332*ef8d499eSDavid van Moolenbroek
333*ef8d499eSDavid van Moolenbroek return OK;
334*ef8d499eSDavid van Moolenbroek }
335*ef8d499eSDavid van Moolenbroek
336*ef8d499eSDavid van Moolenbroek /*
337*ef8d499eSDavid van Moolenbroek * Retrieve and validate a destination address for use in a socket connect or
338*ef8d499eSDavid van Moolenbroek * sendto call. The user-provided address is given as 'addr', with length
339*ef8d499eSDavid van Moolenbroek * 'addr_len'. The socket's current local IP address is given as 'local_addr'.
340*ef8d499eSDavid van Moolenbroek * On success, return OK with the destination IP address stored in 'dst_addr'
341*ef8d499eSDavid van Moolenbroek * and, if 'dst_port' is not NULL, the port number to bind to stored in
342*ef8d499eSDavid van Moolenbroek * 'dst_port'. Otherwise, return a negative error code. This function must be
343*ef8d499eSDavid van Moolenbroek * called, in one way or another, for every destination address used for
344*ef8d499eSDavid van Moolenbroek * connecting or sending on a IP-layer socket.
345*ef8d499eSDavid van Moolenbroek */
346*ef8d499eSDavid van Moolenbroek int
ipsock_get_dst_addr(struct ipsock * ip,const struct sockaddr * addr,socklen_t addr_len,const ip_addr_t * local_addr,ip_addr_t * dst_addr,uint16_t * dst_port)347*ef8d499eSDavid van Moolenbroek ipsock_get_dst_addr(struct ipsock * ip, const struct sockaddr * addr,
348*ef8d499eSDavid van Moolenbroek socklen_t addr_len, const ip_addr_t * local_addr, ip_addr_t * dst_addr,
349*ef8d499eSDavid van Moolenbroek uint16_t * dst_port)
350*ef8d499eSDavid van Moolenbroek {
351*ef8d499eSDavid van Moolenbroek uint16_t port;
352*ef8d499eSDavid van Moolenbroek int r;
353*ef8d499eSDavid van Moolenbroek
354*ef8d499eSDavid van Moolenbroek /* Parse the user-provided address. */
355*ef8d499eSDavid van Moolenbroek if ((r = addr_get_inet(addr, addr_len, ipsock_get_type(ip), dst_addr,
356*ef8d499eSDavid van Moolenbroek FALSE /*kame*/, &port)) != OK)
357*ef8d499eSDavid van Moolenbroek return r;
358*ef8d499eSDavid van Moolenbroek
359*ef8d499eSDavid van Moolenbroek /* Destination addresses are always specific. */
360*ef8d499eSDavid van Moolenbroek if (IP_GET_TYPE(dst_addr) == IPADDR_TYPE_ANY)
361*ef8d499eSDavid van Moolenbroek IP_SET_TYPE(dst_addr, IPADDR_TYPE_V6);
362*ef8d499eSDavid van Moolenbroek
363*ef8d499eSDavid van Moolenbroek /*
364*ef8d499eSDavid van Moolenbroek * lwIP does not support IPv4-mapped IPv6 addresses, so these must be
365*ef8d499eSDavid van Moolenbroek * supported to plain IPv4 addresses instead. In V6ONLY mode, refuse
366*ef8d499eSDavid van Moolenbroek * connecting or sending to IPv4-mapped addresses at all.
367*ef8d499eSDavid van Moolenbroek */
368*ef8d499eSDavid van Moolenbroek if (IP_IS_V6(dst_addr) &&
369*ef8d499eSDavid van Moolenbroek ip6_addr_isipv4mappedipv6(ip_2_ip6(dst_addr))) {
370*ef8d499eSDavid van Moolenbroek if (ipsock_is_v6only(ip))
371*ef8d499eSDavid van Moolenbroek return EINVAL;
372*ef8d499eSDavid van Moolenbroek
373*ef8d499eSDavid van Moolenbroek ip_addr_set_ip4_u32(dst_addr, ip_2_ip6(dst_addr)->addr[3]);
374*ef8d499eSDavid van Moolenbroek }
375*ef8d499eSDavid van Moolenbroek
376*ef8d499eSDavid van Moolenbroek /*
377*ef8d499eSDavid van Moolenbroek * Now make sure that the local and remote addresses are of the same
378*ef8d499eSDavid van Moolenbroek * family. The local address may be of type IPADDR_TYPE_ANY, which is
379*ef8d499eSDavid van Moolenbroek * allowed for both IPv4 and IPv6. Even for connectionless socket
380*ef8d499eSDavid van Moolenbroek * types we must perform this check as part of connect calls (as well
381*ef8d499eSDavid van Moolenbroek * as sendto calls!) because otherwise we will create problems for
382*ef8d499eSDavid van Moolenbroek * sysctl based socket enumeration (i.e., netstat), which uses the
383*ef8d499eSDavid van Moolenbroek * local IP address type to determine the socket family.
384*ef8d499eSDavid van Moolenbroek */
385*ef8d499eSDavid van Moolenbroek if (IP_GET_TYPE(local_addr) != IPADDR_TYPE_ANY &&
386*ef8d499eSDavid van Moolenbroek IP_IS_V6(local_addr) != IP_IS_V6(dst_addr))
387*ef8d499eSDavid van Moolenbroek return EINVAL;
388*ef8d499eSDavid van Moolenbroek
389*ef8d499eSDavid van Moolenbroek /*
390*ef8d499eSDavid van Moolenbroek * TODO: on NetBSD, an 'any' destination address is replaced with a
391*ef8d499eSDavid van Moolenbroek * local interface address.
392*ef8d499eSDavid van Moolenbroek */
393*ef8d499eSDavid van Moolenbroek if (ip_addr_isany(dst_addr))
394*ef8d499eSDavid van Moolenbroek return EHOSTUNREACH;
395*ef8d499eSDavid van Moolenbroek
396*ef8d499eSDavid van Moolenbroek /*
397*ef8d499eSDavid van Moolenbroek * If the address is a multicast address, the multicast address itself
398*ef8d499eSDavid van Moolenbroek * must be valid.
399*ef8d499eSDavid van Moolenbroek */
400*ef8d499eSDavid van Moolenbroek if (ip_addr_ismulticast(dst_addr) &&
401*ef8d499eSDavid van Moolenbroek !addr_is_valid_multicast(dst_addr))
402*ef8d499eSDavid van Moolenbroek return EINVAL;
403*ef8d499eSDavid van Moolenbroek
404*ef8d499eSDavid van Moolenbroek /*
405*ef8d499eSDavid van Moolenbroek * TODO: decide whether to add a zone to a scoped IPv6 address that
406*ef8d499eSDavid van Moolenbroek * lacks a zone. For now, we let lwIP handle this, as lwIP itself
407*ef8d499eSDavid van Moolenbroek * will always add the zone at some point. If anything changes there,
408*ef8d499eSDavid van Moolenbroek * this would be the place to set the zone (using a route lookup).
409*ef8d499eSDavid van Moolenbroek */
410*ef8d499eSDavid van Moolenbroek
411*ef8d499eSDavid van Moolenbroek /*
412*ef8d499eSDavid van Moolenbroek * For now, we do not forbid or alter any other particular destination
413*ef8d499eSDavid van Moolenbroek * addresses.
414*ef8d499eSDavid van Moolenbroek */
415*ef8d499eSDavid van Moolenbroek
416*ef8d499eSDavid van Moolenbroek if (dst_port != NULL) {
417*ef8d499eSDavid van Moolenbroek /*
418*ef8d499eSDavid van Moolenbroek * Disallow connecting/sending to port zero. There is no error
419*ef8d499eSDavid van Moolenbroek * code that applies well to this case, so we copy NetBSD's.
420*ef8d499eSDavid van Moolenbroek */
421*ef8d499eSDavid van Moolenbroek if (port == 0)
422*ef8d499eSDavid van Moolenbroek return EADDRNOTAVAIL;
423*ef8d499eSDavid van Moolenbroek
424*ef8d499eSDavid van Moolenbroek *dst_port = port;
425*ef8d499eSDavid van Moolenbroek }
426*ef8d499eSDavid van Moolenbroek
427*ef8d499eSDavid van Moolenbroek return OK;
428*ef8d499eSDavid van Moolenbroek }
429*ef8d499eSDavid van Moolenbroek
430*ef8d499eSDavid van Moolenbroek /*
431*ef8d499eSDavid van Moolenbroek * Store the address 'ipaddr' associated with the socket 'ip' (for example, it
432*ef8d499eSDavid van Moolenbroek * may be the local or remote IP address of the socket) as a sockaddr structure
433*ef8d499eSDavid van Moolenbroek * in 'addr'. A port number is provided as 'port' (in host-byte order) if
434*ef8d499eSDavid van Moolenbroek * relevant, and zero is passed in otherwise. This function MUST only be
435*ef8d499eSDavid van Moolenbroek * called from contexts where 'addr' is a buffer provided by libsockevent or
436*ef8d499eSDavid van Moolenbroek * libsockdriver, meaning that it is of size SOCKADDR_MAX. The value pointed
437*ef8d499eSDavid van Moolenbroek * to by 'addr_len' is not expected to be initialized in calls to this function
438*ef8d499eSDavid van Moolenbroek * (and will typically zero). On return, 'addr_len' is filled with the length
439*ef8d499eSDavid van Moolenbroek * of the address generated in 'addr'. This function never fails.
440*ef8d499eSDavid van Moolenbroek */
441*ef8d499eSDavid van Moolenbroek void
ipsock_put_addr(struct ipsock * ip,struct sockaddr * addr,socklen_t * addr_len,ip_addr_t * ipaddr,uint16_t port)442*ef8d499eSDavid van Moolenbroek ipsock_put_addr(struct ipsock * ip, struct sockaddr * addr,
443*ef8d499eSDavid van Moolenbroek socklen_t * addr_len, ip_addr_t * ipaddr, uint16_t port)
444*ef8d499eSDavid van Moolenbroek {
445*ef8d499eSDavid van Moolenbroek ip_addr_t mappedaddr;
446*ef8d499eSDavid van Moolenbroek
447*ef8d499eSDavid van Moolenbroek /*
448*ef8d499eSDavid van Moolenbroek * If the socket is an AF_INET6-type socket, and the given address is
449*ef8d499eSDavid van Moolenbroek * an IPv4-type address, store it as an IPv4-mapped IPv6 address.
450*ef8d499eSDavid van Moolenbroek */
451*ef8d499eSDavid van Moolenbroek if (ipsock_is_ipv6(ip) && IP_IS_V4(ipaddr)) {
452*ef8d499eSDavid van Moolenbroek addr_make_v4mapped_v6(&mappedaddr, ip_2_ip4(ipaddr));
453*ef8d499eSDavid van Moolenbroek
454*ef8d499eSDavid van Moolenbroek ipaddr = &mappedaddr;
455*ef8d499eSDavid van Moolenbroek }
456*ef8d499eSDavid van Moolenbroek
457*ef8d499eSDavid van Moolenbroek /*
458*ef8d499eSDavid van Moolenbroek * We have good reasons to keep the sockdriver and sockevent APIs as
459*ef8d499eSDavid van Moolenbroek * they are, namely, defaulting 'addr_len' to zero such that the caller
460*ef8d499eSDavid van Moolenbroek * must provide a non-zero length (only) when returning a valid
461*ef8d499eSDavid van Moolenbroek * address. The consequence here is that we have to know the size of
462*ef8d499eSDavid van Moolenbroek * the provided buffer. For libsockevent callbacks, we are always
463*ef8d499eSDavid van Moolenbroek * guaranteed to get a buffer of at least this size.
464*ef8d499eSDavid van Moolenbroek */
465*ef8d499eSDavid van Moolenbroek *addr_len = SOCKADDR_MAX;
466*ef8d499eSDavid van Moolenbroek
467*ef8d499eSDavid van Moolenbroek addr_put_inet(addr, addr_len, ipaddr, FALSE /*kame*/, port);
468*ef8d499eSDavid van Moolenbroek }
469*ef8d499eSDavid van Moolenbroek
470*ef8d499eSDavid van Moolenbroek /*
471*ef8d499eSDavid van Moolenbroek * Set socket options on an IP socket.
472*ef8d499eSDavid van Moolenbroek */
473*ef8d499eSDavid van Moolenbroek int
ipsock_setsockopt(struct ipsock * ip,int level,int name,const struct sockdriver_data * data,socklen_t len,struct ipopts * ipopts)474*ef8d499eSDavid van Moolenbroek ipsock_setsockopt(struct ipsock * ip, int level, int name,
475*ef8d499eSDavid van Moolenbroek const struct sockdriver_data * data, socklen_t len,
476*ef8d499eSDavid van Moolenbroek struct ipopts * ipopts)
477*ef8d499eSDavid van Moolenbroek {
478*ef8d499eSDavid van Moolenbroek int r, val, allow;
479*ef8d499eSDavid van Moolenbroek uint8_t type;
480*ef8d499eSDavid van Moolenbroek
481*ef8d499eSDavid van Moolenbroek switch (level) {
482*ef8d499eSDavid van Moolenbroek case SOL_SOCKET:
483*ef8d499eSDavid van Moolenbroek switch (name) {
484*ef8d499eSDavid van Moolenbroek case SO_SNDBUF:
485*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
486*ef8d499eSDavid van Moolenbroek len)) != OK)
487*ef8d499eSDavid van Moolenbroek return r;
488*ef8d499eSDavid van Moolenbroek
489*ef8d499eSDavid van Moolenbroek if (val <= 0 || (size_t)val < ipopts->sndmin ||
490*ef8d499eSDavid van Moolenbroek (size_t)val > ipopts->sndmax)
491*ef8d499eSDavid van Moolenbroek return EINVAL;
492*ef8d499eSDavid van Moolenbroek
493*ef8d499eSDavid van Moolenbroek ip->ip_sndbuf = val;
494*ef8d499eSDavid van Moolenbroek
495*ef8d499eSDavid van Moolenbroek return OK;
496*ef8d499eSDavid van Moolenbroek
497*ef8d499eSDavid van Moolenbroek case SO_RCVBUF:
498*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
499*ef8d499eSDavid van Moolenbroek len)) != OK)
500*ef8d499eSDavid van Moolenbroek return r;
501*ef8d499eSDavid van Moolenbroek
502*ef8d499eSDavid van Moolenbroek if (val <= 0 || (size_t)val < ipopts->rcvmin ||
503*ef8d499eSDavid van Moolenbroek (size_t)val > ipopts->rcvmax)
504*ef8d499eSDavid van Moolenbroek return EINVAL;
505*ef8d499eSDavid van Moolenbroek
506*ef8d499eSDavid van Moolenbroek ip->ip_rcvbuf = val;
507*ef8d499eSDavid van Moolenbroek
508*ef8d499eSDavid van Moolenbroek return OK;
509*ef8d499eSDavid van Moolenbroek }
510*ef8d499eSDavid van Moolenbroek
511*ef8d499eSDavid van Moolenbroek break;
512*ef8d499eSDavid van Moolenbroek
513*ef8d499eSDavid van Moolenbroek case IPPROTO_IP:
514*ef8d499eSDavid van Moolenbroek if (ipsock_is_ipv6(ip))
515*ef8d499eSDavid van Moolenbroek break;
516*ef8d499eSDavid van Moolenbroek
517*ef8d499eSDavid van Moolenbroek switch (name) {
518*ef8d499eSDavid van Moolenbroek case IP_TOS:
519*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
520*ef8d499eSDavid van Moolenbroek len)) != OK)
521*ef8d499eSDavid van Moolenbroek return r;
522*ef8d499eSDavid van Moolenbroek
523*ef8d499eSDavid van Moolenbroek if (val < 0 || val > UINT8_MAX)
524*ef8d499eSDavid van Moolenbroek return EINVAL;
525*ef8d499eSDavid van Moolenbroek
526*ef8d499eSDavid van Moolenbroek *ipopts->tos = (uint8_t)val;
527*ef8d499eSDavid van Moolenbroek
528*ef8d499eSDavid van Moolenbroek return OK;
529*ef8d499eSDavid van Moolenbroek
530*ef8d499eSDavid van Moolenbroek case IP_TTL:
531*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
532*ef8d499eSDavid van Moolenbroek len)) != OK)
533*ef8d499eSDavid van Moolenbroek return r;
534*ef8d499eSDavid van Moolenbroek
535*ef8d499eSDavid van Moolenbroek if (val < 0 || val > UINT8_MAX)
536*ef8d499eSDavid van Moolenbroek return EINVAL;
537*ef8d499eSDavid van Moolenbroek
538*ef8d499eSDavid van Moolenbroek *ipopts->ttl = (uint8_t)val;
539*ef8d499eSDavid van Moolenbroek
540*ef8d499eSDavid van Moolenbroek return OK;
541*ef8d499eSDavid van Moolenbroek }
542*ef8d499eSDavid van Moolenbroek
543*ef8d499eSDavid van Moolenbroek break;
544*ef8d499eSDavid van Moolenbroek
545*ef8d499eSDavid van Moolenbroek case IPPROTO_IPV6:
546*ef8d499eSDavid van Moolenbroek if (!ipsock_is_ipv6(ip))
547*ef8d499eSDavid van Moolenbroek break;
548*ef8d499eSDavid van Moolenbroek
549*ef8d499eSDavid van Moolenbroek switch (name) {
550*ef8d499eSDavid van Moolenbroek case IPV6_UNICAST_HOPS:
551*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
552*ef8d499eSDavid van Moolenbroek len)) != OK)
553*ef8d499eSDavid van Moolenbroek return r;
554*ef8d499eSDavid van Moolenbroek
555*ef8d499eSDavid van Moolenbroek if (val < -1 || val > UINT8_MAX)
556*ef8d499eSDavid van Moolenbroek return EINVAL;
557*ef8d499eSDavid van Moolenbroek
558*ef8d499eSDavid van Moolenbroek if (val == -1)
559*ef8d499eSDavid van Moolenbroek val = IP_DEFAULT_TTL;
560*ef8d499eSDavid van Moolenbroek
561*ef8d499eSDavid van Moolenbroek *ipopts->ttl = val;
562*ef8d499eSDavid van Moolenbroek
563*ef8d499eSDavid van Moolenbroek return OK;
564*ef8d499eSDavid van Moolenbroek
565*ef8d499eSDavid van Moolenbroek case IPV6_TCLASS:
566*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
567*ef8d499eSDavid van Moolenbroek len)) != OK)
568*ef8d499eSDavid van Moolenbroek return r;
569*ef8d499eSDavid van Moolenbroek
570*ef8d499eSDavid van Moolenbroek if (val < -1 || val > UINT8_MAX)
571*ef8d499eSDavid van Moolenbroek return EINVAL;
572*ef8d499eSDavid van Moolenbroek
573*ef8d499eSDavid van Moolenbroek if (val == -1)
574*ef8d499eSDavid van Moolenbroek val = 0;
575*ef8d499eSDavid van Moolenbroek
576*ef8d499eSDavid van Moolenbroek *ipopts->tos = val;
577*ef8d499eSDavid van Moolenbroek
578*ef8d499eSDavid van Moolenbroek return OK;
579*ef8d499eSDavid van Moolenbroek
580*ef8d499eSDavid van Moolenbroek case IPV6_V6ONLY:
581*ef8d499eSDavid van Moolenbroek if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
582*ef8d499eSDavid van Moolenbroek len)) != OK)
583*ef8d499eSDavid van Moolenbroek return r;
584*ef8d499eSDavid van Moolenbroek
585*ef8d499eSDavid van Moolenbroek /*
586*ef8d499eSDavid van Moolenbroek * If the socket has been bound to an actual address,
587*ef8d499eSDavid van Moolenbroek * we still allow the option to be changed, but it no
588*ef8d499eSDavid van Moolenbroek * longer has any effect.
589*ef8d499eSDavid van Moolenbroek */
590*ef8d499eSDavid van Moolenbroek type = IP_GET_TYPE(ipopts->local_ip);
591*ef8d499eSDavid van Moolenbroek allow = (type == IPADDR_TYPE_ANY ||
592*ef8d499eSDavid van Moolenbroek (type == IPADDR_TYPE_V6 &&
593*ef8d499eSDavid van Moolenbroek ip_addr_isany(ipopts->local_ip)));
594*ef8d499eSDavid van Moolenbroek
595*ef8d499eSDavid van Moolenbroek if (val) {
596*ef8d499eSDavid van Moolenbroek ip->ip_flags |= IPF_V6ONLY;
597*ef8d499eSDavid van Moolenbroek
598*ef8d499eSDavid van Moolenbroek type = IPADDR_TYPE_V6;
599*ef8d499eSDavid van Moolenbroek } else {
600*ef8d499eSDavid van Moolenbroek ip->ip_flags &= ~IPF_V6ONLY;
601*ef8d499eSDavid van Moolenbroek
602*ef8d499eSDavid van Moolenbroek type = IPADDR_TYPE_ANY;
603*ef8d499eSDavid van Moolenbroek }
604*ef8d499eSDavid van Moolenbroek
605*ef8d499eSDavid van Moolenbroek if (allow)
606*ef8d499eSDavid van Moolenbroek IP_SET_TYPE(ipopts->local_ip, type);
607*ef8d499eSDavid van Moolenbroek
608*ef8d499eSDavid van Moolenbroek return OK;
609*ef8d499eSDavid van Moolenbroek }
610*ef8d499eSDavid van Moolenbroek
611*ef8d499eSDavid van Moolenbroek break;
612*ef8d499eSDavid van Moolenbroek }
613*ef8d499eSDavid van Moolenbroek
614*ef8d499eSDavid van Moolenbroek return ENOPROTOOPT;
615*ef8d499eSDavid van Moolenbroek }
616*ef8d499eSDavid van Moolenbroek
617*ef8d499eSDavid van Moolenbroek /*
618*ef8d499eSDavid van Moolenbroek * Retrieve socket options on an IP socket.
619*ef8d499eSDavid van Moolenbroek */
620*ef8d499eSDavid van Moolenbroek int
ipsock_getsockopt(struct ipsock * ip,int level,int name,const struct sockdriver_data * data,socklen_t * len,struct ipopts * ipopts)621*ef8d499eSDavid van Moolenbroek ipsock_getsockopt(struct ipsock * ip, int level, int name,
622*ef8d499eSDavid van Moolenbroek const struct sockdriver_data * data, socklen_t * len,
623*ef8d499eSDavid van Moolenbroek struct ipopts * ipopts)
624*ef8d499eSDavid van Moolenbroek {
625*ef8d499eSDavid van Moolenbroek int val;
626*ef8d499eSDavid van Moolenbroek
627*ef8d499eSDavid van Moolenbroek switch (level) {
628*ef8d499eSDavid van Moolenbroek case SOL_SOCKET:
629*ef8d499eSDavid van Moolenbroek switch (name) {
630*ef8d499eSDavid van Moolenbroek case SO_SNDBUF:
631*ef8d499eSDavid van Moolenbroek val = ip->ip_sndbuf;
632*ef8d499eSDavid van Moolenbroek
633*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
634*ef8d499eSDavid van Moolenbroek len);
635*ef8d499eSDavid van Moolenbroek
636*ef8d499eSDavid van Moolenbroek case SO_RCVBUF:
637*ef8d499eSDavid van Moolenbroek val = ip->ip_rcvbuf;
638*ef8d499eSDavid van Moolenbroek
639*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
640*ef8d499eSDavid van Moolenbroek len);
641*ef8d499eSDavid van Moolenbroek }
642*ef8d499eSDavid van Moolenbroek
643*ef8d499eSDavid van Moolenbroek break;
644*ef8d499eSDavid van Moolenbroek
645*ef8d499eSDavid van Moolenbroek case IPPROTO_IP:
646*ef8d499eSDavid van Moolenbroek if (ipsock_is_ipv6(ip))
647*ef8d499eSDavid van Moolenbroek break;
648*ef8d499eSDavid van Moolenbroek
649*ef8d499eSDavid van Moolenbroek switch (name) {
650*ef8d499eSDavid van Moolenbroek case IP_TOS:
651*ef8d499eSDavid van Moolenbroek val = (int)*ipopts->tos;
652*ef8d499eSDavid van Moolenbroek
653*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
654*ef8d499eSDavid van Moolenbroek len);
655*ef8d499eSDavid van Moolenbroek
656*ef8d499eSDavid van Moolenbroek case IP_TTL:
657*ef8d499eSDavid van Moolenbroek val = (int)*ipopts->ttl;
658*ef8d499eSDavid van Moolenbroek
659*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
660*ef8d499eSDavid van Moolenbroek len);
661*ef8d499eSDavid van Moolenbroek }
662*ef8d499eSDavid van Moolenbroek
663*ef8d499eSDavid van Moolenbroek break;
664*ef8d499eSDavid van Moolenbroek
665*ef8d499eSDavid van Moolenbroek case IPPROTO_IPV6:
666*ef8d499eSDavid van Moolenbroek if (!ipsock_is_ipv6(ip))
667*ef8d499eSDavid van Moolenbroek break;
668*ef8d499eSDavid van Moolenbroek
669*ef8d499eSDavid van Moolenbroek switch (name) {
670*ef8d499eSDavid van Moolenbroek case IPV6_UNICAST_HOPS:
671*ef8d499eSDavid van Moolenbroek val = *ipopts->ttl;
672*ef8d499eSDavid van Moolenbroek
673*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
674*ef8d499eSDavid van Moolenbroek len);
675*ef8d499eSDavid van Moolenbroek
676*ef8d499eSDavid van Moolenbroek case IPV6_TCLASS:
677*ef8d499eSDavid van Moolenbroek val = *ipopts->tos;
678*ef8d499eSDavid van Moolenbroek
679*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
680*ef8d499eSDavid van Moolenbroek len);
681*ef8d499eSDavid van Moolenbroek
682*ef8d499eSDavid van Moolenbroek case IPV6_V6ONLY:
683*ef8d499eSDavid van Moolenbroek val = !!(ip->ip_flags & IPF_V6ONLY);
684*ef8d499eSDavid van Moolenbroek
685*ef8d499eSDavid van Moolenbroek return sockdriver_copyout_opt(data, &val, sizeof(val),
686*ef8d499eSDavid van Moolenbroek len);
687*ef8d499eSDavid van Moolenbroek }
688*ef8d499eSDavid van Moolenbroek
689*ef8d499eSDavid van Moolenbroek break;
690*ef8d499eSDavid van Moolenbroek }
691*ef8d499eSDavid van Moolenbroek
692*ef8d499eSDavid van Moolenbroek return ENOPROTOOPT;
693*ef8d499eSDavid van Moolenbroek }
694*ef8d499eSDavid van Moolenbroek
695*ef8d499eSDavid van Moolenbroek /*
696*ef8d499eSDavid van Moolenbroek * Fill the given kinfo_pcb sysctl(7) structure with IP-level information.
697*ef8d499eSDavid van Moolenbroek */
698*ef8d499eSDavid van Moolenbroek void
ipsock_get_info(struct kinfo_pcb * ki,const ip_addr_t * local_ip,uint16_t local_port,const ip_addr_t * remote_ip,uint16_t remote_port)699*ef8d499eSDavid van Moolenbroek ipsock_get_info(struct kinfo_pcb * ki, const ip_addr_t * local_ip,
700*ef8d499eSDavid van Moolenbroek uint16_t local_port, const ip_addr_t * remote_ip, uint16_t remote_port)
701*ef8d499eSDavid van Moolenbroek {
702*ef8d499eSDavid van Moolenbroek ip_addr_t ipaddr;
703*ef8d499eSDavid van Moolenbroek socklen_t len;
704*ef8d499eSDavid van Moolenbroek uint8_t type;
705*ef8d499eSDavid van Moolenbroek
706*ef8d499eSDavid van Moolenbroek len = sizeof(ki->ki_spad); /* use this for the full size, not ki_src */
707*ef8d499eSDavid van Moolenbroek
708*ef8d499eSDavid van Moolenbroek addr_put_inet(&ki->ki_src, &len, local_ip, TRUE /*kame*/, local_port);
709*ef8d499eSDavid van Moolenbroek
710*ef8d499eSDavid van Moolenbroek /*
711*ef8d499eSDavid van Moolenbroek * At this point, the local IP address type has already been used to
712*ef8d499eSDavid van Moolenbroek * determine whether this is an IPv4 or IPv6 socket. While not ideal,
713*ef8d499eSDavid van Moolenbroek * that is the best we can do: we cannot use IPv4-mapped IPv6 addresses
714*ef8d499eSDavid van Moolenbroek * in lwIP PCBs, we cannot store the original type in those PCBs, and
715*ef8d499eSDavid van Moolenbroek * we also cannot rely on the PCB having an associated ipsock object
716*ef8d499eSDavid van Moolenbroek * anymore. We also cannot use the ipsock only when present: it could
717*ef8d499eSDavid van Moolenbroek * make a TCP PCB "jump" from IPv6 to IPv4 in the netstat listing when
718*ef8d499eSDavid van Moolenbroek * it goes into TIME_WAIT state, for example.
719*ef8d499eSDavid van Moolenbroek *
720*ef8d499eSDavid van Moolenbroek * So, use *only* the type of the local IP address to determine whether
721*ef8d499eSDavid van Moolenbroek * this is an IPv4 or an IPv6 socket. At the same time, do *not* rely
722*ef8d499eSDavid van Moolenbroek * on the remote IP address being IPv4 for a local IPv4 address; it may
723*ef8d499eSDavid van Moolenbroek * be of type IPADDR_TYPE_V6 for an unconnected socket bound to an
724*ef8d499eSDavid van Moolenbroek * IPv4-mapped IPv6 address. Pretty messy, but we're limited by what
725*ef8d499eSDavid van Moolenbroek * lwIP offers here. Since it's just netstat, it need not be perfect.
726*ef8d499eSDavid van Moolenbroek */
727*ef8d499eSDavid van Moolenbroek if ((type = IP_GET_TYPE(local_ip)) == IPADDR_TYPE_V4) {
728*ef8d499eSDavid van Moolenbroek if (!ip_addr_isany(local_ip) || local_port != 0)
729*ef8d499eSDavid van Moolenbroek ki->ki_prstate = INP_BOUND;
730*ef8d499eSDavid van Moolenbroek
731*ef8d499eSDavid van Moolenbroek /*
732*ef8d499eSDavid van Moolenbroek * Make sure the returned socket address types are consistent.
733*ef8d499eSDavid van Moolenbroek * The only case where the remote IP address is not IPv4 here
734*ef8d499eSDavid van Moolenbroek * is when it is not set yet, so there is no need to check
735*ef8d499eSDavid van Moolenbroek * whether it is the 'any' address: it always is.
736*ef8d499eSDavid van Moolenbroek */
737*ef8d499eSDavid van Moolenbroek if (IP_GET_TYPE(remote_ip) != IPADDR_TYPE_V4) {
738*ef8d499eSDavid van Moolenbroek ip_addr_set_zero_ip4(&ipaddr);
739*ef8d499eSDavid van Moolenbroek
740*ef8d499eSDavid van Moolenbroek remote_ip = &ipaddr;
741*ef8d499eSDavid van Moolenbroek }
742*ef8d499eSDavid van Moolenbroek } else {
743*ef8d499eSDavid van Moolenbroek if (!ip_addr_isany(local_ip) || local_port != 0)
744*ef8d499eSDavid van Moolenbroek ki->ki_prstate = IN6P_BOUND;
745*ef8d499eSDavid van Moolenbroek if (type != IPADDR_TYPE_ANY)
746*ef8d499eSDavid van Moolenbroek ki->ki_pflags |= IN6P_IPV6_V6ONLY;
747*ef8d499eSDavid van Moolenbroek }
748*ef8d499eSDavid van Moolenbroek
749*ef8d499eSDavid van Moolenbroek len = sizeof(ki->ki_dpad); /* use this for the full size, not ki_dst */
750*ef8d499eSDavid van Moolenbroek
751*ef8d499eSDavid van Moolenbroek addr_put_inet(&ki->ki_dst, &len, remote_ip, TRUE /*kame*/,
752*ef8d499eSDavid van Moolenbroek remote_port);
753*ef8d499eSDavid van Moolenbroek
754*ef8d499eSDavid van Moolenbroek /* Check the type of the *local* IP address here. See above. */
755*ef8d499eSDavid van Moolenbroek if (!ip_addr_isany(remote_ip) || remote_port != 0) {
756*ef8d499eSDavid van Moolenbroek if (type == IPADDR_TYPE_V4)
757*ef8d499eSDavid van Moolenbroek ki->ki_prstate = INP_CONNECTED;
758*ef8d499eSDavid van Moolenbroek else
759*ef8d499eSDavid van Moolenbroek ki->ki_prstate = IN6P_CONNECTED;
760*ef8d499eSDavid van Moolenbroek }
761*ef8d499eSDavid van Moolenbroek }
762