Index: etc/services =================================================================== RCS file: /cvs/src/etc/services,v retrieving revision 1.96 diff -u -p -r1.96 services --- etc/services 27 Jan 2019 20:35:06 -0000 1.96 +++ etc/services 29 Oct 2019 07:57:44 -0000 @@ -332,6 +332,21 @@ spamd-cfg 8026/tcp # spamd(8) configur dhcpd-sync 8067/udp # dhcpd(8) synchronisation hunt 26740/udp # hunt(6) # +# GRE Protocol Types +# +keepalive 0/gre # 0x0000: IP tunnel keepalive +ipv4 2048/gre # 0x0800: IPv4 +nhrp 8193/gre # 0x2001: Next Hop Resolution Protocol +erspan3 8939/gre # 0x22eb: ERSPAN III +transether 25944/gre ethernet # 0x6558: Trans Ether Bridging +ipv6 34525/gre # 0x86dd: IPv6 +wccp 34878/gre # 0x883e: Web Content Cache Protocol +mpls 34887/gre # 0x8847: MPLS +#mpls 34888/gre # 0x8848: MPLS Multicast +erspan 35006/gre erspan2 # 0x88be: ERSPAN I/II +nsh 35151/gre # 0x894f: Network Service Header +control 47082/gre # 0xb7ea: RFC 8157 +# # Appletalk # rtmp 1/ddp # Routing Table Maintenance Protocol Index: lib/libc/asr/getaddrinfo_async.c =================================================================== RCS file: /cvs/src/lib/libc/asr/getaddrinfo_async.c,v retrieving revision 1.56 diff -u -p -r1.56 getaddrinfo_async.c --- lib/libc/asr/getaddrinfo_async.c 3 Nov 2018 09:13:24 -0000 1.56 +++ lib/libc/asr/getaddrinfo_async.c 29 Oct 2019 07:57:54 -0000 @@ -34,36 +34,15 @@ #include "asr_private.h" -struct match { - int family; - int socktype; - int protocol; -}; - static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); static int get_port(const char *, const char *, int); +static int get_service(const char *, int, int); static int iter_family(struct asr_query *, int); static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); static int addrinfo_from_file(struct asr_query *, int, FILE *); static int addrinfo_from_pkt(struct asr_query *, char *, size_t); static int addrconfig_setup(struct asr_query *); -static const struct match matches[] = { - { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, - { PF_INET, SOCK_STREAM, IPPROTO_TCP }, - { PF_INET, SOCK_RAW, 0 }, - { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, - { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, - { PF_INET6, SOCK_RAW, 0 }, - { -1, 0, 0, }, -}; - -#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) -#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) -/* Do not match SOCK_RAW unless explicitly specified */ -#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ - matches[(b)].socktype != SOCK_RAW)) - enum { DOM_INIT, DOM_DOMAIN, @@ -199,24 +178,27 @@ getaddrinfo_async_run(struct asr_query * } } - /* Make sure there is at least a valid combination */ - for (i = 0; matches[i].family != -1; i++) - if (MATCH_FAMILY(ai->ai_family, i) && - MATCH_SOCKTYPE(ai->ai_socktype, i) && - MATCH_PROTO(ai->ai_protocol, i)) - break; - if (matches[i].family == -1) { - ar->ar_gai_errno = EAI_BADHINTS; - async_set_state(as, ASR_STATE_HALT); - break; - } - - if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) + switch (ai->ai_protocol) { + case 0: as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", as->as.ai.hints.ai_flags & AI_NUMERICSERV); - if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", as->as.ai.hints.ai_flags & AI_NUMERICSERV); + break; + case IPPROTO_TCP: + as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + break; + case IPPROTO_UDP: + as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + break; + default: + as->as.ai.port_udp = get_service(as->as.ai.servname, + ai->ai_protocol, + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + break; + } if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || (ai->ai_protocol && (as->as.ai.port_udp == -1 || @@ -491,6 +473,24 @@ get_port(const char *servname, const cha return (port); } +static int +get_service(const char *servname, int protocol, int numonly) +{ + struct protoent pe; + struct protoent_data ped; + int rv; + + memset(&ped, 0, sizeof(ped)); + rv = getprotobynumber_r(protocol, &pe, &ped); + if (rv == -1) + return (-1); + + rv = get_port(servname, pe.p_name, numonly); + endprotoent_r(&ped); + + return (rv); +} + /* * Iterate over the address families that are to be queried. Use the * list on the async context, unless a specific family was given in hints. @@ -519,65 +519,107 @@ iter_family(struct asr_query *as, int fi * entry per protocol/socktype match. */ static int -addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) +addrinfo_add_ai(struct asr_query *as, const struct sockaddr *sa, + const char *cname, int socktype, int proto, int port) { struct addrinfo *ai; - int i, port, proto; - - for (i = 0; matches[i].family != -1; i++) { - if (matches[i].family != sa->sa_family || - !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || - !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) - continue; - - proto = as->as.ai.hints.ai_protocol; - if (!proto) - proto = matches[i].protocol; - - if (proto == IPPROTO_TCP) - port = as->as.ai.port_tcp; - else if (proto == IPPROTO_UDP) - port = as->as.ai.port_udp; - else - port = 0; + int i; - /* servname specified, but not defined for this protocol */ - if (port == -1) - continue; + if (port == -1) + return (0); - ai = calloc(1, sizeof(*ai) + sa->sa_len); - if (ai == NULL) + ai = calloc(1, sizeof(*ai) + sa->sa_len); + if (ai == NULL) + return (EAI_MEMORY); + ai->ai_family = sa->sa_family; + ai->ai_socktype = socktype; + ai->ai_protocol = proto; + ai->ai_flags = as->as.ai.hints.ai_flags; + ai->ai_addrlen = sa->sa_len; + ai->ai_addr = (void *)(ai + 1); + if (cname && + as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { + if ((ai->ai_canonname = strdup(cname)) == NULL) { + free(ai); return (EAI_MEMORY); - ai->ai_family = sa->sa_family; - ai->ai_socktype = matches[i].socktype; - ai->ai_protocol = proto; - ai->ai_flags = as->as.ai.hints.ai_flags; - ai->ai_addrlen = sa->sa_len; - ai->ai_addr = (void *)(ai + 1); - if (cname && - as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { - if ((ai->ai_canonname = strdup(cname)) == NULL) { - free(ai); - return (EAI_MEMORY); - } } - memmove(ai->ai_addr, sa, sa->sa_len); - if (sa->sa_family == PF_INET) - ((struct sockaddr_in *)ai->ai_addr)->sin_port = - htons(port); - else if (sa->sa_family == PF_INET6) - ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = - htons(port); - - if (as->as.ai.aifirst == NULL) - as->as.ai.aifirst = ai; - if (as->as.ai.ailast) - as->as.ai.ailast->ai_next = ai; - as->as.ai.ailast = ai; - as->as_count += 1; } + memmove(ai->ai_addr, sa, sa->sa_len); + if (sa->sa_family == PF_INET) + ((struct sockaddr_in *)ai->ai_addr)->sin_port = + htons(port); + else if (sa->sa_family == PF_INET6) + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = + htons(port); + + if (as->as.ai.aifirst == NULL) + as->as.ai.aifirst = ai; + if (as->as.ai.ailast) + as->as.ai.ailast->ai_next = ai; + as->as.ai.ailast = ai; + as->as_count += 1; return (0); +} + +static int +addrinfo_add_proto(struct asr_query *as, const struct sockaddr *sa, + const char *cname, int proto, int port) +{ + int rv; + + switch (as->as.ai.hints.ai_socktype) { + case 0: + rv = addrinfo_add_ai(as, sa, cname, SOCK_STREAM, proto, port); + if (rv != 0) + break; + + rv = addrinfo_add_ai(as, sa, cname, SOCK_DGRAM, proto, port); + if (rv != 0) + break; + + break; + + default: + rv = addrinfo_add_ai(as, sa, cname, + as->as.ai.hints.ai_socktype, proto, port); + break; + } + + return (rv); +} + +static int +addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) +{ + int rv; + + switch (as->as.ai.hints.ai_protocol) { + case 0: + rv = addrinfo_add_proto(as, sa, cname, + IPPROTO_TCP, as->as.ai.port_tcp); + if (rv != 0) + break; + + rv = addrinfo_add_proto(as, sa, cname, + IPPROTO_UDP, as->as.ai.port_udp); + if (rv != 0) + break; + + break; + + case IPPROTO_TCP: + rv = addrinfo_add_proto(as, sa, cname, + IPPROTO_TCP, as->as.ai.port_tcp); + break; + + default: /* includes IPPROTO_UDP */ + rv = addrinfo_add_proto(as, sa, cname, + as->as.ai.hints.ai_protocol, as->as.ai.port_udp); + break; + } + + return (rv); } static int Index: sys/conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.675 diff -u -p -r1.675 files --- sys/conf/files 5 Oct 2019 05:33:14 -0000 1.675 +++ sys/conf/files 29 Oct 2019 07:57:58 -0000 @@ -862,7 +862,7 @@ file netinet/tcp_subr.c file netinet/tcp_timer.c file netinet/tcp_usrreq.c file netinet/udp_usrreq.c -file netinet/ip_gre.c +file netinet/ip_gre.c gre file netinet/ip_ipsp.c ipsec | tcp_signature file netinet/ip_spd.c ipsec | tcp_signature file netinet/ip_ipip.c Index: sys/kern/kern_pledge.c =================================================================== RCS file: /cvs/src/sys/kern/kern_pledge.c,v retrieving revision 1.255 diff -u -p -r1.255 kern_pledge.c --- sys/kern/kern_pledge.c 25 Aug 2019 18:46:40 -0000 1.255 +++ sys/kern/kern_pledge.c 29 Oct 2019 07:57:58 -0000 @@ -666,7 +666,7 @@ pledge_namei(struct proc *p, struct name } } - /* DNS needs /etc/{resolv.conf,hosts,services}. */ + /* DNS needs /etc/{resolv.conf,hosts,services,protocols}. */ if ((ni->ni_pledge == PLEDGE_RPATH) && (p->p_p->ps_pledge & PLEDGE_DNS)) { if (strcmp(path, "/etc/resolv.conf") == 0) { @@ -678,6 +678,10 @@ pledge_namei(struct proc *p, struct name return (0); } if (strcmp(path, "/etc/services") == 0) { + ni->ni_cnd.cn_flags |= BYPASSUNVEIL; + return (0); + } + if (strcmp(path, "/etc/protocols") == 0) { ni->ni_cnd.cn_flags |= BYPASSUNVEIL; return (0); } Index: sys/net/if_gre.c =================================================================== RCS file: /cvs/src/sys/net/if_gre.c,v retrieving revision 1.152 diff -u -p -r1.152 if_gre.c --- sys/net/if_gre.c 29 Jul 2019 16:28:25 -0000 1.152 +++ sys/net/if_gre.c 29 Oct 2019 07:57:58 -0000 @@ -69,6 +69,8 @@ #include #include +#include + #ifdef INET6 #include #include @@ -103,28 +105,6 @@ /* * packet formats */ -struct gre_header { - uint16_t gre_flags; -#define GRE_CP 0x8000 /* Checksum Present */ -#define GRE_KP 0x2000 /* Key Present */ -#define GRE_SP 0x1000 /* Sequence Present */ - -#define GRE_VERS_MASK 0x0007 -#define GRE_VERS_0 0x0000 -#define GRE_VERS_1 0x0001 - - uint16_t gre_proto; -} __packed __aligned(4); - -struct gre_h_cksum { - uint16_t gre_cksum; - uint16_t gre_reserved1; -} __packed __aligned(4); - -struct gre_h_key { - uint32_t gre_key; -} __packed __aligned(4); - #define GRE_EOIP 0x6400 struct gre_h_key_eoip { @@ -132,13 +112,7 @@ struct gre_h_key_eoip { uint16_t eoip_tunnel_id; /* little endian */ } __packed __aligned(4); -#define NVGRE_VSID_RES_MIN 0x000000 /* reserved for future use */ -#define NVGRE_VSID_RES_MAX 0x000fff -#define NVGRE_VSID_NVE2NVE 0xffffff /* vendor specific NVE-to-NVE comms */ - -struct gre_h_seq { - uint32_t gre_seq; -} __packed __aligned(4); +#define GRE_WCCP 0x883e struct gre_h_wccp { uint8_t wccp_flags; @@ -147,7 +121,10 @@ struct gre_h_wccp { uint8_t pri_bucket; } __packed __aligned(4); -#define GRE_WCCP 0x883e + +#define NVGRE_VSID_RES_MIN 0x000000 /* reserved for future use */ +#define NVGRE_VSID_RES_MAX 0x000fff +#define NVGRE_VSID_NVE2NVE 0xffffff /* vendor specific NVE-to-NVE comms */ #define GRE_HDRLEN (sizeof(struct ip) + sizeof(struct gre_header)) @@ -289,8 +266,8 @@ static int gre_up(struct gre_softc *); static int gre_down(struct gre_softc *); static void gre_link_state(struct ifnet *, unsigned int); -static int gre_input_key(struct mbuf **, int *, int, int, uint8_t, - struct gre_tunnel *); +static struct mbuf * + gre_if_input(struct mbuf *, int, uint8_t, struct gre_tunnel *); static struct mbuf * gre_ipv4_patch(const struct gre_tunnel *, struct mbuf *, @@ -893,10 +870,9 @@ eoip_clone_destroy(struct ifnet *ifp) return (0); } -int -gre_input(struct mbuf **mp, int *offp, int type, int af) +struct mbuf * +gre_if4_input(struct mbuf *m, int hlen) { - struct mbuf *m = *mp; struct gre_tunnel key; struct ip *ip; @@ -908,17 +884,13 @@ gre_input(struct mbuf **mp, int *offp, i key.t_src4 = ip->ip_dst; key.t_dst4 = ip->ip_src; - if (gre_input_key(mp, offp, type, af, ip->ip_tos, &key) == -1) - return (rip_input(mp, offp, type, af)); - - return (IPPROTO_DONE); + return (gre_if_input(m, hlen, ip->ip_tos, &key)); } #ifdef INET6 -int -gre_input6(struct mbuf **mp, int *offp, int type, int af) +struct mbuf * +gre_if6_input(struct mbuf *m, int hlen) { - struct mbuf *m = *mp; struct gre_tunnel key; struct ip6_hdr *ip6; uint32_t flow; @@ -933,10 +905,7 @@ gre_input6(struct mbuf **mp, int *offp, flow = bemtoh32(&ip6->ip6_flow); - if (gre_input_key(mp, offp, type, af, flow >> 20, &key) == -1) - return (rip6_input(mp, offp, type, af)); - - return (IPPROTO_DONE); + return (gre_if_input(m, hlen, flow >> 20, &key)); } #endif /* INET6 */ @@ -996,12 +965,10 @@ gre_input_1(struct gre_tunnel *key, stru return (m); } -static int -gre_input_key(struct mbuf **mp, int *offp, int type, int af, uint8_t otos, - struct gre_tunnel *key) +static struct mbuf * +gre_if_input(struct mbuf *m, int iphlen, uint8_t otos, struct gre_tunnel *key) { - struct mbuf *m = *mp; - int iphlen = *offp, hlen, rxprio; + int hlen, rxprio; struct ifnet *ifp; const struct gre_tunnel *tunnel; caddr_t buf; @@ -1025,7 +992,7 @@ gre_input_key(struct mbuf **mp, int *off m = m_pullup(m, hlen); if (m == NULL) - return (IPPROTO_DONE); + return (NULL); buf = mtod(m, caddr_t); gh = (struct gre_header *)(buf + iphlen); @@ -1038,7 +1005,7 @@ gre_input_key(struct mbuf **mp, int *off case htons(GRE_VERS_1): m = gre_input_1(key, m, gh, otos, iphlen); if (m == NULL) - return (IPPROTO_DONE); + return (NULL); /* FALLTHROUGH */ default: goto decline; @@ -1055,7 +1022,7 @@ gre_input_key(struct mbuf **mp, int *off m = m_pullup(m, hlen); if (m == NULL) - return (IPPROTO_DONE); + return (NULL); buf = mtod(m, caddr_t); gh = (struct gre_header *)(buf + iphlen); @@ -1071,7 +1038,7 @@ gre_input_key(struct mbuf **mp, int *off nvgre_input(key, m, hlen, otos) == -1) goto decline; - return (IPPROTO_DONE); + return (NULL); } ifp = gre_find(key); @@ -1148,7 +1115,7 @@ gre_input_key(struct mbuf **mp, int *off m_adj(m, hlen); gre_keepalive_recv(ifp, m); - return (IPPROTO_DONE); + return (NULL); default: goto decline; @@ -1162,7 +1129,7 @@ gre_input_key(struct mbuf **mp, int *off m = (*patch)(tunnel, m, &itos, otos); if (m == NULL) - return (IPPROTO_DONE); + return (NULL); if (tunnel->t_key_mask == GRE_KEY_ENTROPY) { m->m_pkthdr.ph_flowid = M_FLOWID_VALID | @@ -1203,10 +1170,9 @@ gre_input_key(struct mbuf **mp, int *off #endif (*input)(ifp, m); - return (IPPROTO_DONE); + return (NULL); decline: - *mp = m; - return (-1); + return (m); } static struct mbuf * Index: sys/netinet/gre_proto.h =================================================================== RCS file: sys/netinet/gre_proto.h diff -N sys/netinet/gre_proto.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/netinet/gre_proto.h 29 Oct 2019 07:57:58 -0000 @@ -0,0 +1,48 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _NETINET_GRE_H_ +#define _NETINET_GRE_H_ + +struct gre_header { + uint16_t gre_flags; +#define GRE_CP 0x8000 /* Checksum Present */ +#define GRE_KP 0x2000 /* Key Present */ +#define GRE_SP 0x1000 /* Sequence Present */ + +#define GRE_VERS_MASK 0x0007 +#define GRE_VERS_0 0x0000 +#define GRE_VERS_1 0x0001 + + uint16_t gre_proto; +}; + +struct gre_h_cksum { + uint16_t gre_cksum; + uint16_t gre_reserved1; +}; + +struct gre_h_key { + uint32_t gre_key; +}; + +struct gre_h_seq { + uint32_t gre_seq; +}; + +#endif /* _NETINET_GRE_H_ */ Index: sys/netinet/gre_var.h =================================================================== RCS file: sys/netinet/gre_var.h diff -N sys/netinet/gre_var.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/netinet/gre_var.h 29 Oct 2019 07:57:58 -0000 @@ -0,0 +1,64 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _NETINET_GRE_VAR_H_ +#define _NETINET_GRE_VAR_H_ + +/* + * setsockopt(s, IPPROTO_GRE, ... + */ + +#define GRE_CKSUM 1 /* bool; enable GRE checksum headers */ +#define GRE_KEY 2 /* uint32_t; enable and set GRE key */ + /* NULL; disable GRE key header */ +#define GRE_SEQ 3 /* uint32_t; enable and set GRE seq */ + /* NULL; disable GRE seq header */ + +#define GRE_RECVSEQ 4 /* bool; enable reception of seq numbers */ +#define GRE_SENDSEQ GRE_RECVSEQ + +#ifdef _KERNEL +int gre_raw_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *); +int gre_sysctl(int *, u_int, void *, size_t *, void *, size_t); + +void gre_init(void); + +int gre_attach(struct socket *, int); +int gre_detach(struct socket *); + +int gre_ip4_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *); +int gre_ip4_ctloutput(int, struct socket *, int, int, struct mbuf *); + +int gre_ip4_input(struct mbuf **, int *, int, int); + +struct mbuf * + gre_if4_input(struct mbuf *, int); /* interface glue */ + +#ifdef INET6 +int gre_ip6_usrreq(struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *); +int gre_ip6_ctloutput(int, struct socket *, int, int, struct mbuf *); +int gre_ip6_input(struct mbuf **, int *, int, int); + +struct mbuf * + gre_if6_input(struct mbuf *, int); /* interface glue */ +#endif /* INET6 */ +#endif /* _KERNEL */ +#endif /* _NETINET_GRE_VAR_H_ */ Index: sys/netinet/in_proto.c =================================================================== RCS file: /cvs/src/sys/netinet/in_proto.c,v retrieving revision 1.93 diff -u -p -r1.93 in_proto.c --- sys/netinet/in_proto.c 15 Jul 2019 12:40:42 -0000 1.93 +++ sys/netinet/in_proto.c 29 Oct 2019 07:57:58 -0000 @@ -147,8 +147,7 @@ #include "gre.h" #if NGRE > 0 -#include -#include +#include #endif #include "carp.h" @@ -346,11 +345,24 @@ const struct protosw inetsw[] = { .pr_domain = &inetdomain, .pr_protocol = IPPROTO_GRE, .pr_flags = PR_ATOMIC|PR_ADDR, - .pr_input = gre_input, + .pr_input = gre_ip4_input, .pr_ctloutput = rip_ctloutput, - .pr_usrreq = gre_usrreq, + .pr_usrreq = gre_raw_usrreq, .pr_attach = rip_attach, .pr_detach = rip_detach, + .pr_sysctl = gre_sysctl +}, +{ + .pr_type = SOCK_DGRAM, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_GRE, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = gre_ip4_input, + .pr_ctloutput = gre_ip4_ctloutput, + .pr_usrreq = gre_ip4_usrreq, + .pr_attach = gre_attach, + .pr_detach = gre_detach, + .pr_init = gre_init, .pr_sysctl = gre_sysctl }, #endif /* NGRE > 0 */ Index: sys/netinet/ip_gre.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_gre.c,v retrieving revision 1.71 diff -u -p -r1.71 ip_gre.c --- sys/netinet/ip_gre.c 7 Feb 2018 22:30:59 -0000 1.71 +++ sys/netinet/ip_gre.c 29 Oct 2019 07:57:58 -0000 @@ -1,7 +1,23 @@ -/* $OpenBSD: ip_gre.c,v 1.71 2018/02/07 22:30:59 dlg Exp $ */ +/* $OpenBSD: ip_gre.c,v 1.71 2018/02/07 22:30:59 dlg Exp $ */ /* $NetBSD: ip_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */ /* + * Copyright (c) 2019 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * @@ -30,16 +46,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* - * decapsulate tunneled packets and send them on - * output half is in net/if_gre.[ch] - * This currently handles IPPROTO_GRE, IPPROTO_MOBILE - */ - - -#include "gre.h" -#if NGRE > 0 - #include #include #include @@ -47,24 +53,40 @@ #include #include #include +#include +#include +#include +#include + +#include #include +#include #include #include +#include #include #include #include +#include +#include + #ifdef PIPEX #include #endif + +/* + * socket({AF_INET,AF_INET6}, SOCK_RAW, IPPROTO_GRE); + */ + int -gre_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, +gre_raw_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, struct mbuf *control, struct proc *p) { -#ifdef PIPEX +#ifdef PIPEX struct inpcb *inp = sotoinpcb(so); if (inp != NULL && inp->inp_pipex && req == PRU_SEND) { @@ -92,4 +114,1817 @@ gre_usrreq(struct socket *so, int req, s #endif return rip_usrreq(so, req, m, nam, control, p); } -#endif /* if NGRE > 0 */ + +/* + * support socket({AF_INET,AF_INET6}, SOCK_DGRAM, IPPROTO_GRE); + * + * GRE datagram sockets provide support for GRE version 0 packets + * in the kernel. + */ + +#define GRE_VALID_MASK (GRE_VERS_MASK | GRE_CP | GRE_KP | GRE_SP) + +struct gre_pcb_key; + +struct gre_pcb { + struct inpcb gpcb_inpcb; + unsigned int gpcb_pflags; /* pcb flags */ +#define GREPCB_RECVSEQ (1 << 0) + uint16_t gpcb_flags; /* network byteorder */ + uint32_t gpcb_key; /* network byteorder */ + uint32_t gpcb_seq; /* host byteorder */ + + struct gre_pcb_key *gpcb_pcb_key; + int gpcb_reuse; + uint8_t gpcb_ttl; +}; +TAILQ_HEAD(gre_pcb_list, inpcb); + +static inline struct gre_pcb * +inp_gpcb(struct inpcb *inp) +{ + return ((struct gre_pcb *)inp); +} + +#define gpcb_inp(_gpcb) (&(_gpcb)->gpcb_inpcb) + +static inline struct socket * +gpcb_so(struct gre_pcb *gpcb) +{ + return (gpcb_inp(gpcb)->inp_socket); +} + +struct gre_pcb_key { + union inpaddru gk_laddr; +#define gk_laddr4 gk_laddr.iau_a4u.inaddr +#define gk_laddr6 gk_laddr.iau_addr6 + union inpaddru gk_faddr; +#define gk_faddr4 gk_faddr.iau_a4u.inaddr +#define gk_faddr6 gk_faddr.iau_addr6 + + uint16_t gk_flags; /* network byteorder */ + uint16_t gk_proto; /* network byteorder */ + uint32_t gk_key; /* network byteorder */ + + unsigned int gk_rtableid; + sa_family_t gk_family; + + RBT_ENTRY(gre_pcb_key) gk_entry; + struct gre_pcb_list gk_pcbs; + unsigned int gk_state; +#define GRE_S_DISCONNECTED 0 +#define GRE_S_WILDCARD 1 +#define GRE_S_BOUND 2 +#define GRE_S_CONNECTED 3 +}; + +RBT_HEAD(gre_tree_wildcards, gre_pcb_key); +RBT_HEAD(gre_tree_bound, gre_pcb_key); +RBT_HEAD(gre_tree_connected, gre_pcb_key); + +static int gre_pcb_key_cmp_wildcard(const struct gre_pcb_key *, + const struct gre_pcb_key *); +static int gre_pcb_key_cmp_bound(const struct gre_pcb_key *, + const struct gre_pcb_key *); + +RBT_PROTOTYPE(gre_tree_wildcards, gre_pcb_key, gk_entry, + gre_pcb_key_cmp_wildcard); +RBT_PROTOTYPE(gre_tree_bound, gre_pcb_key, gk_entry, + gre_pcb_key_cmp_bound); +RBT_PROTOTYPE(gre_tree_connected, gre_pcb_key, gk_entry, + gre_pcb_key_cmp_connected); + +struct gre_ops { + int (*op_nametosa)(struct inpcb *, struct mbuf *, + struct sockaddr **); + int (*op_is_wildcard)(struct sockaddr *); + int (*op_is_multicast)(struct sockaddr *); + int (*op_is_broadcast)(unsigned int, struct sockaddr *); + int (*op_is_local)(unsigned int, struct sockaddr *); + + uint16_t (*op_proto)(struct sockaddr *); + void (*op_addr)(union inpaddru *, struct sockaddr *); + + int (*op_selsrc)(struct inpcb *, struct sockaddr *, + union inpaddru *, void *); + int (*op_control)(struct socket *, u_long, caddr_t, struct ifnet *); + void (*op_getsockname)(struct inpcb *, struct mbuf *); + void (*op_getpeername)(struct inpcb *, struct mbuf *); + int (*op_ctloutput)(int, struct socket *, int, int, struct mbuf *); + + void (*op_sbappend)(struct gre_pcb *, struct mbuf *, int, uint32_t); + + /* PRU_SEND is split into two parts, with gre_output in the middle: */ + + /* 1: the "pre" phase, so ipv6 can do it's stupid pktops stuff */ + int (*op_send)(const struct gre_ops *, struct gre_pcb *, + struct mbuf *, struct mbuf *, struct mbuf *); + /* 2: actually doing the ip encap and output */ + int (*op_output)(struct gre_pcb *, const struct gre_pcb_key *, + struct mbuf *, void *); + + int * const defttl; +}; +struct gre_tree_wildcards gre_wildcards = RBT_INITIALIZER(); +struct gre_tree_bound gre_bound = RBT_INITIALIZER(); +struct gre_tree_connected gre_connected = RBT_INITIALIZER(); + +struct pool gre_pcb_key_pool; +struct pool gre_pcb_pool; + +static struct mbuf * + gre_ip_input(const struct gre_ops *, struct mbuf *, int, + uint8_t, struct gre_pcb_key *); + +static int gre_usrreq(const struct gre_ops *, struct socket *, int, + struct mbuf *, struct mbuf *, struct mbuf *, + struct proc *); +static int gre_ctloutput(const struct gre_ops *, int, struct socket *, + int, int, struct mbuf *); + +static int gre_send(const struct gre_ops *, struct gre_pcb *, + struct mbuf *, struct mbuf *, struct mbuf *); +static int gre_output(const struct gre_ops *, struct gre_pcb *, + struct mbuf *, struct mbuf *, struct mbuf *, void *); +static int gre_disconnect(struct gre_pcb *); + +#define GRE_OPT_EINVAL ((void *)-1) +static void *gre_opt(struct mbuf *, int, int, socklen_t); + +unsigned int gre_sendspace = 9216; /* XXX sysctl? */ +unsigned int gre_recvspace = (40 * 1024); + +static struct gre_pcb_key * +gre_pcb_key_get(const struct gre_pcb *gpcb) +{ + struct gre_pcb_key *gk; + + gk = pool_get(&gre_pcb_key_pool, PR_NOWAIT|PR_ZERO); + if (gk == NULL) + return (NULL); + + gk->gk_flags = gpcb->gpcb_flags; + gk->gk_key = gpcb->gpcb_key; + + TAILQ_INIT(&gk->gk_pcbs); + + return (gk); +} + +static void +gre_pcb_key_put(struct gre_pcb_key *gk) +{ + pool_put(&gre_pcb_key_pool, gk); +} + +static struct gre_pcb_key * +gre_pcb_key_insert(unsigned int state, struct gre_pcb_key *gk) +{ + struct gre_pcb_key *ogk; + + gk->gk_state = state; + + switch (state) { + case GRE_S_WILDCARD: + ogk = RBT_INSERT(gre_tree_wildcards, &gre_wildcards, gk); + break; + case GRE_S_BOUND: + ogk = RBT_INSERT(gre_tree_bound, &gre_bound, gk); + break; + case GRE_S_CONNECTED: + ogk = RBT_INSERT(gre_tree_connected, &gre_connected, gk); + break; + default: + panic("%s unexpected state %u", __func__, state); + } + + return (ogk); +} + +static inline int +gre_pcb_empty(struct gre_pcb_list *l) +{ + return (TAILQ_EMPTY(l)); +} + +static inline void +gre_pcb_insert(struct gre_pcb_list *l, struct gre_pcb *gpcb) +{ + TAILQ_INSERT_TAIL(l, gpcb_inp(gpcb), inp_queue); +} + +static inline void +gre_pcb_remove(struct gre_pcb_list *l, struct gre_pcb *gpcb) +{ + TAILQ_REMOVE(l, gpcb_inp(gpcb), inp_queue); +} + +static inline struct gre_pcb * +gre_pcb_first(struct gre_pcb_list *l) +{ + return (inp_gpcb(TAILQ_FIRST(l))); +} + +static inline struct gre_pcb * +gre_pcb_next(struct gre_pcb *gpcb) +{ + return (inp_gpcb(TAILQ_NEXT(gpcb_inp(gpcb), inp_queue))); +} + +/* + * INET4 + */ + +static int gre_ip4_nametosa(struct inpcb *, struct mbuf *, + struct sockaddr **); +static int gre_ip4_is_wildcard(struct sockaddr *); +static int gre_ip4_is_multicast(struct sockaddr *); +static int gre_ip4_is_broadcast(unsigned int, struct sockaddr *); +static int gre_ip4_is_local(unsigned int, struct sockaddr *); +static uint16_t gre_ip4_proto(struct sockaddr *); +static void gre_ip4_addr(union inpaddru *, struct sockaddr *); +static int gre_ip4_selsrc(struct inpcb *, struct sockaddr *, + union inpaddru *, void *); +static void gre_ip4_sbappend(struct gre_pcb *, struct mbuf *, int, + uint32_t); +static int gre_ip4_send(const struct gre_ops *, struct gre_pcb *, + struct mbuf *, struct mbuf *, struct mbuf *); +static int gre_ip4_output(struct gre_pcb *, const struct gre_pcb_key *, + struct mbuf *, void *); + +static const struct gre_ops gre_ip4_ops = { + .op_nametosa = gre_ip4_nametosa, + .op_is_wildcard = gre_ip4_is_wildcard, + .op_is_multicast = gre_ip4_is_multicast, + .op_is_broadcast = gre_ip4_is_broadcast, + .op_is_local = gre_ip4_is_local, + + .op_proto = gre_ip4_proto, + .op_addr = gre_ip4_addr, + + .op_selsrc = gre_ip4_selsrc, + .op_control = in_control, + .op_getsockname = in_setsockaddr, + .op_getpeername = in_setpeeraddr, + .op_ctloutput = ip_ctloutput, + + .op_sbappend = gre_ip4_sbappend, + .op_send = gre_ip4_send, + .op_output = gre_ip4_output, + + .defttl = &ip_defttl, +}; + + +static int +gre_ip4_nametosa(struct inpcb *inp, struct mbuf *addr, struct sockaddr **sa) +{ + return (in_nam2sin(addr, (struct sockaddr_in **)sa)); +} + +static int +gre_ip4_is_wildcard(struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + return (sin->sin_addr.s_addr == INADDR_ANY); +} + +static int +gre_ip4_is_multicast(struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + return (IN_MULTICAST(sin->sin_addr.s_addr)); +} + +static int +gre_ip4_is_broadcast(unsigned int rtableid, struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + + return ((sin->sin_addr.s_addr == INADDR_BROADCAST) || + in_broadcast(sin->sin_addr, rtableid)); +} + +static int +gre_ip4_is_local(unsigned int rtableid, struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + /* cope with ifa_ifwithaddr using memcmp */ + struct sockaddr_in needle = { + .sin_len = sin->sin_len, + .sin_family = sin->sin_family, + .sin_addr = sin->sin_addr, + }; + struct ifaddr *ifa; + + ifa = ifa_ifwithaddr(sintosa(&needle), rtableid); + if (ifa == NULL) + return (EADDRNOTAVAIL); + + return (0); +} + +static uint16_t +gre_ip4_proto(struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + return (sin->sin_port); +} + +static void +gre_ip4_addr(union inpaddru *addr, struct sockaddr *sa) +{ + struct sockaddr_in *sin = satosin(sa); + addr->iau_a4u.inaddr = sin->sin_addr; +} + +static int +gre_ip4_selsrc(struct inpcb *inp, struct sockaddr *sa, union inpaddru *laddr, + void *opts) +{ + struct in_addr *insrc; + int error; + + insrc = gre_opt(opts, IPPROTO_IP, IP_SENDSRCADDR, sizeof(*insrc)); + if (insrc == GRE_OPT_EINVAL) + return (EINVAL); + + if (insrc == NULL) { + error = in_pcbselsrc(&insrc, satosin(sa), inp); + if (error != 0) + return (error); + } else { + struct sockaddr_in sin = { + .sin_len = sizeof(sin), + .sin_family = AF_INET, + .sin_addr = *insrc, + }; + + /* XXX sigh. */ + error = in_pcbaddrisavail(inp, &sin, 0, NULL); + if (error != 0) + return (error); + } + + laddr->iau_a4u.inaddr = *insrc; + + return (0); +} + +static int +gre_ip4_send(const struct gre_ops *ops, struct gre_pcb *gpcb, + struct mbuf *m, struct mbuf *addr, struct mbuf *control) +{ + return (gre_output(ops, gpcb, m, addr, control, control)); +} + +static int +gre_ip4_output(struct gre_pcb *gpcb, const struct gre_pcb_key *gk, + struct mbuf *m, void *opts) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct ip *ip; + int error; + uint32_t ipsecflowinfo = 0; + + m = m_prepend(m, sizeof(*ip), M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + goto dropped; + } + if (m->m_pkthdr.len > IP_MAXPACKET) { + error = EMSGSIZE; + goto drop; + } + +#ifdef IPSEC + if (ISSET(inp->inp_flags, INP_IPSECFLOWINFO)) { + uint32_t *p = gre_opt(opts, IPPROTO_IP, IP_IPSECFLOWINFO, + sizeof(*p)); + if (p == GRE_OPT_EINVAL) { + error = EINVAL; + goto drop; + } + + if (p != NULL) + ipsecflowinfo = *p; + } +#endif /* IPSEC */ + + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_off = 0; /* XXX nodf? */ + ip->ip_tos = 0; /* XXX */; + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_ttl = gpcb->gpcb_ttl; + ip->ip_p = IPPROTO_GRE; + ip->ip_src = gk->gk_laddr4; + ip->ip_dst = gk->gk_faddr4; + + error = ip_output(m, inp->inp_options, &inp->inp_route, + gpcb_so(gpcb)->so_options & SO_BROADCAST, inp->inp_moptions, inp, + ipsecflowinfo); + + return (error); + +drop: + m_freem(m); +dropped: + return (error); +} + +static void * +gre_opt(struct mbuf *control, int level, int type, socklen_t len) +{ + u_int clen; + struct cmsghdr *cm; + caddr_t cmsgs; + size_t mlen; + + if (control == NULL) + return (NULL); + + if (control->m_next != NULL) + return (GRE_OPT_EINVAL); + + mlen = CMSG_LEN(len); + + clen = control->m_len; + cmsgs = mtod(control, caddr_t); + do { + if (clen < CMSG_LEN(0)) + return (GRE_OPT_EINVAL); + + cm = (struct cmsghdr *)cmsgs; + if (cm->cmsg_len < CMSG_LEN(0) || + CMSG_ALIGN(cm->cmsg_len) > clen) + return (GRE_OPT_EINVAL); + + if (cm->cmsg_level == level && + cm->cmsg_type == type && + cm->cmsg_len == mlen) + return (CMSG_DATA(cm)); + + clen -= CMSG_ALIGN(cm->cmsg_len); + cmsgs += CMSG_ALIGN(cm->cmsg_len); + } while (clen); + + return (NULL); +} + +static int +gre_send(const struct gre_ops *ops, struct gre_pcb *gpcb, + struct mbuf *m, struct mbuf *addr, struct mbuf *control) +{ + int error; + + error = (*ops->op_send)(ops, gpcb, m, addr, control); + + m_freem(control); + + return (error); +} + +static int +gre_output(const struct gre_ops *ops, struct gre_pcb *gpcb, + struct mbuf *m, struct mbuf *addr, struct mbuf *control, void *opts) +{ + const struct gre_pcb_key *gk = gpcb->gpcb_pcb_key; + struct gre_pcb_key key; + struct gre_header *gh; + int error; + + if (addr != NULL) { + struct gre_pcb_key *ogk; + struct inpcb *inp = gpcb_inp(gpcb); + struct sockaddr *sa; + int state = GRE_S_DISCONNECTED; + + if (gk != NULL) + state = gk->gk_state; + + if (state == GRE_S_CONNECTED) { + error = EISCONN; + goto drop; + } + + error = (*ops->op_nametosa)(inp, addr, &sa); + if (error != 0) + goto drop; + + key.gk_family = sa->sa_family; + key.gk_proto = (*ops->op_proto)(sa); + + if (state != GRE_S_DISCONNECTED) { + KASSERT(key.gk_family == gk->gk_family); + if (key.gk_proto != gk->gk_proto) { + error = EADDRNOTAVAIL; + goto drop; + } + } + if (state == GRE_S_BOUND) { + key.gk_laddr = gk->gk_laddr; + } else { + error = (*ops->op_selsrc)(inp, sa, &key.gk_laddr, + opts); + if (error != 0) + goto drop; + } + + (*ops->op_addr)(&key.gk_faddr, sa); + + key.gk_rtableid = inp->inp_rtableid; + key.gk_flags = gpcb->gpcb_flags; + key.gk_key = gpcb->gpcb_key; + + ogk = RBT_FIND(gre_tree_connected, &gre_connected, &key); + if (ogk != NULL) { + struct gre_pcb *ogpcb; + int reuse = gpcb->gpcb_reuse; + + ogpcb = gre_pcb_first(&ogk->gk_pcbs); + if (ogpcb != NULL) { + struct socket *oso = gpcb_so(ogpcb); + if (!ISSET(reuse, oso->so_options)) { + error = EADDRINUSE; + goto drop; + } + } + } + + gk = &key; + } else { + if (gk == NULL || gk->gk_state != GRE_S_CONNECTED) { + error = ENOTCONN; + goto drop; + } + } + + if (ISSET(gk->gk_flags, htons(GRE_SP))) { + struct gre_h_seq *gsh; + uint32_t *seqno; + + seqno = gre_opt(control, IPPROTO_GRE, GRE_SENDSEQ, + sizeof(*seqno)); + if (seqno == GRE_OPT_EINVAL) { + error = EINVAL; + goto drop; + } + + m = m_prepend(m, sizeof(*gsh), M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + + gsh = mtod(m, struct gre_h_seq *); + htobem32(&gsh->gre_seq, seqno != NULL ? + (gpcb->gpcb_seq = *seqno) : /* keep track of new start */ + atomic_inc_int_nv(&gpcb->gpcb_seq)); + } + + if (ISSET(gk->gk_flags, htons(GRE_KP))) { + struct gre_h_key *gkh; + + m = m_prepend(m, sizeof(*gkh), M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + + gkh = mtod(m, struct gre_h_key *); + gkh->gre_key = gk->gk_key; + } + + if (ISSET(gk->gk_flags, htons(GRE_CP))) { + struct gre_h_cksum *gch; + + m = m_prepend(m, sizeof(*gch), M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + + gch = mtod(m, struct gre_h_cksum *); + gch->gre_cksum = 0; /* XXX need to checksum */ + gch->gre_reserved1 = 0; + } + + m = m_prepend(m, sizeof(*gh), M_DONTWAIT); + if (m == NULL) + return (ENOBUFS); + + gh = mtod(m, struct gre_header *); + gh->gre_flags = gk->gk_flags; + gh->gre_proto = gk->gk_proto; + + KASSERT(ISSET(m->m_flags, M_PKTHDR)); + + m->m_pkthdr.ph_rtableid = gpcb_inp(gpcb)->inp_rtableid; + + return ((*ops->op_output)(gpcb, gk, m, opts)); + +drop: + m_freem(m); + return (error); +} + +static void +gre_sbappend(struct gre_pcb *gpcb, struct sockaddr *sa, struct mbuf *m, + struct mbuf *opts, int hlen, uint32_t seqno) +{ + struct socket *so = gpcb_so(gpcb); + + if (ISSET(gpcb->gpcb_pflags, GREPCB_RECVSEQ)) { + struct mbuf *opt = sbcreatecontrol(&seqno, sizeof(seqno), + GRE_RECVSEQ, IPPROTO_GRE); + if (opt != NULL) { + opt->m_next = opts; + opts = opt; + } + } + + m_adj(m, hlen); + if (sbappendaddr(so, &so->so_rcv, sa, m, opts) == 0) { + m_freem(m); + m_freem(opts); + return; + } + + sorwakeup(so); +} + +int +gre_ip4_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr, + struct mbuf *control, struct proc *p) +{ + return (gre_usrreq(&gre_ip4_ops, so, req, m, addr, control, p)); +} + +int +gre_ip4_ctloutput(int op, struct socket *so, int level, int optname, + struct mbuf *m) +{ + return (gre_ctloutput(&gre_ip4_ops, op, so, level, optname, m)); +} + +static void +gre_ip4_sbappend(struct gre_pcb *gpcb, struct mbuf *m, int hlen, uint32_t seqno) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct socket *so = gpcb_so(gpcb); + struct mbuf *opts = NULL; + struct sockaddr_in sin = { + .sin_len = sizeof(sin), + .sin_family = AF_INET, + .sin_port = inp->inp_lport, + .sin_addr = mtod(m, struct ip *)->ip_src, + }; + + if (ISSET(inp->inp_flags, INP_CONTROLOPTS) || + ISSET(so->so_options, SO_TIMESTAMP)) + ip_savecontrol(inp, &opts, mtod(m, struct ip *), m); + + if (ISSET(inp->inp_flags, INP_RECVDSTPORT)) { + struct mbuf *opt; + + opt = sbcreatecontrol(&inp->inp_lport, sizeof(inp->inp_lport), + IP_RECVDSTPORT, IPPROTO_IP); + if (opt != NULL) { + opt->m_next = opts; + opts = opt; + } + } + + gre_sbappend(gpcb, sintosa(&sin), m, opts, hlen, seqno); +} + +int +gre_ip4_input(struct mbuf **mp, int *offp, int proto, int af) +{ + struct mbuf *m = *mp; + struct gre_pcb_key gk; + struct ip *ip; + + m = gre_if4_input(m, *offp); + if (m == NULL) + return (IPPROTO_DONE); + + ip = mtod(m, struct ip *); + + gk.gk_family = AF_INET; + gk.gk_laddr4 = ip->ip_dst; + gk.gk_faddr4 = ip->ip_src; + + m = gre_ip_input(&gre_ip4_ops, m, *offp, ip->ip_ttl, &gk); + if (m == NULL) + return (IPPROTO_DONE); + + *mp = m; + return (rip_input(mp, offp, proto, af)); +} + +#if INET6 +#include +#include + +static int gre_ip6_nametosa(struct inpcb *, struct mbuf *, + struct sockaddr **); +static int gre_ip6_is_wildcard(struct sockaddr *); +static int gre_ip6_is_multicast(struct sockaddr *); +static int gre_ip6_is_broadcast(unsigned int, struct sockaddr *); +static int gre_ip6_is_local(unsigned int, struct sockaddr *); +static uint16_t gre_ip6_proto(struct sockaddr *); +static void gre_ip6_addr(union inpaddru *, struct sockaddr *); +static int gre_ip6_selsrc(struct inpcb *, struct sockaddr *, + union inpaddru *, void *); +static void gre_ip6_sbappend(struct gre_pcb *, struct mbuf *, int, + uint32_t); +static int gre_ip6_send(const struct gre_ops *, struct gre_pcb *, + struct mbuf *, struct mbuf *, struct mbuf *); +static int gre_ip6_output(struct gre_pcb *, const struct gre_pcb_key *, + struct mbuf *, void *); + +static const struct gre_ops gre_ip6_ops = { + .op_nametosa = gre_ip6_nametosa, + .op_is_wildcard = gre_ip6_is_wildcard, + .op_is_multicast = gre_ip6_is_multicast, + .op_is_broadcast = gre_ip6_is_broadcast, + .op_is_local = gre_ip6_is_local, + + .op_proto = gre_ip6_proto, + .op_addr = gre_ip6_addr, + + .op_selsrc = gre_ip6_selsrc, + .op_control = in6_control, + .op_getsockname = in6_setsockaddr, + .op_getpeername = in6_setpeeraddr, + .op_ctloutput = ip6_ctloutput, + .op_sbappend = gre_ip6_sbappend, + .op_send = gre_ip6_send, + .op_output = gre_ip6_output, + + .defttl = &ip6_defhlim, +}; + +static int +gre_ip6_nametosa(struct inpcb *inp, struct mbuf *addr, struct sockaddr **sa) +{ + struct sockaddr_in6 *sin6; + int error; + + error = in6_nam2sin6(addr, &sin6); + if (error != 0) + return (error); + + /* reject IPv4 mapped addresses, we have no support for them */ + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + return (EADDRNOTAVAIL); + + if (in6_embedscope(&sin6->sin6_addr, sin6, inp) != 0) + return (EINVAL); + + *sa = sin6tosa(sin6); + return (0); +} + +static int +gre_ip6_is_wildcard(struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6 = satosin6(sa); + return (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)); +} + +static int +gre_ip6_is_multicast(struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6 = satosin6(sa); + return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)); +} + +static int +gre_ip6_is_broadcast(unsigned int rtableid, struct sockaddr *sa) +{ + return (0); +} + +static int +gre_ip6_is_local(unsigned int rtableid, struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6 = satosin6(sa); + struct sockaddr_in6 needle = { + .sin6_len = sin6->sin6_len, + .sin6_family = sin6->sin6_family, + .sin6_addr = sin6->sin6_addr, + }; + struct ifaddr *ifa; + + ifa = ifa_ifwithaddr(sin6tosa(&needle), rtableid); + if (ifa == NULL) + return (EADDRNOTAVAIL); + + /* + * bind to an anycast address might accidentally + * cause sending a packet with an anycast source + * address, so we forbid it. + * + * We should allow to bind to a deprecated address, + * since the application dare to use it. + * But, can we assume that they are careful enough + * to check if the address is deprecated or not? + * Maybe, as a safeguard, we should have a setsockopt + * flag to control the bind(2) behavior against + * deprecated addresses (default: forbid bind(2)). + */ + if (ISSET(ifatoia6(ifa)->ia6_flags, IN6_IFF_ANYCAST| + IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED|IN6_IFF_DETACHED)) + return (EADDRNOTAVAIL); + + return (0); +} + +static uint16_t +gre_ip6_proto(struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6 = satosin6(sa); + return (sin6->sin6_port); +} + +static void +gre_ip6_addr(union inpaddru *addr, struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6 = satosin6(sa); + addr->iau_addr6 = sin6->sin6_addr; +} + +static int +gre_ip6_selsrc(struct inpcb *inp, struct sockaddr *sa, union inpaddru *laddr, + void *opts) +{ + struct in6_addr *in6src; + int error; + + if (opts == NULL) + opts = inp->inp_outputopts6; + + error = in6_pcbselsrc(&in6src, satosin6(sa), inp, opts); + if (error != 0) + return (error); + + laddr->iau_addr6 = *in6src; + + return (0); +} + +int +gre_ip6_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr, + struct mbuf *control, struct proc *p) +{ + return (gre_usrreq(&gre_ip6_ops, so, req, m, addr, control, p)); +} + +int +gre_ip6_ctloutput(int op, struct socket *so, int level, int optname, + struct mbuf *m) +{ + return (gre_ctloutput(&gre_ip6_ops, op, so, level, optname, m)); +} + +static void +gre_ip6_sbappend(struct gre_pcb *gpcb, struct mbuf *m, int hlen, uint32_t seqno) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct socket *so = gpcb_so(gpcb); + struct mbuf *opts = NULL; + struct sockaddr_in6 sin6 = { + .sin6_len = sizeof(sin6), + .sin6_family = AF_INET6, + .sin6_port = inp->inp_lport, + }; + + in6_recoverscope(&sin6, &mtod(m, struct ip6_hdr *)->ip6_src); + + if (ISSET(inp->inp_flags, IN6P_CONTROLOPTS) || + ISSET(so->so_options, SO_TIMESTAMP)) + ip6_savecontrol(inp, m, &opts); + + if (ISSET(inp->inp_flags, IN6P_RECVDSTPORT)) { + struct mbuf *opt; + + opt = sbcreatecontrol(&inp->inp_fport, sizeof(inp->inp_fport), + IPV6_RECVDSTPORT, IPPROTO_IPV6); + if (opt != NULL) { + opt->m_next = opts; + opts = opt; + } + } + + gre_sbappend(gpcb, sin6tosa(&sin6), m, opts, hlen, seqno); +} + +static int +gre_ip6_send(const struct gre_ops *ops, struct gre_pcb *gpcb, struct mbuf *m, + struct mbuf *addr, struct mbuf *control) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct ip6_pktopts *opts = inp->inp_outputopts6; + struct ip6_pktopts opt; + int error; + + if (control != NULL) { + error = ip6_setpktopts(control, &opt, opts, /* priv */ 1, + IPPROTO_GRE); + if (error != 0) { + m_freem(m); + return (error); + } + opts = &opt; + } + + error = gre_output(ops, gpcb, m, addr, control, opts); + if (control != NULL) + ip6_clearpktopts(&opt, -1); + return (error); +} + +static int +gre_ip6_output(struct gre_pcb *gpcb, const struct gre_pcb_key *gk, + struct mbuf *m, void *opts) +{ + struct inpcb *inp = gpcb_inp(gpcb); + uint16_t len = m->m_pkthdr.len; + struct ip6_hdr *ip6; + int flags = 0; + int error; + + if (len > IP_MAXPACKET) { + error = EMSGSIZE; + goto drop; + } + + m = m_prepend(m, sizeof(*ip6), M_DONTWAIT); + if (m == NULL) { + error = ENOBUFS; + goto dropped; + } + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_plen = htons(len); + ip6->ip6_nxt = IPPROTO_GRE; + ip6->ip6_hlim = gpcb->gpcb_ttl; + ip6->ip6_src = gk->gk_laddr6; + ip6->ip6_dst = gk->gk_faddr6; + + if (ISSET(inp->inp_flags, IN6P_MINMTU)) /* wtf */ + flags |= IPV6_MINMTU; + + error = ip6_output(m, opts, &inp->inp_route6, + flags, inp->inp_moptions6, inp); + + return (error); + +drop: + m_freem(m); +dropped: + return (error); +} + +int +gre_ip6_input(struct mbuf **mp, int *offp, int proto, int af) +{ + struct mbuf *m = *mp; + struct gre_pcb_key gk; + struct ip6_hdr *ip6; + + m = gre_if6_input(m, *offp); + if (m == NULL) + return (IPPROTO_DONE); + + ip6 = mtod(m, struct ip6_hdr *); + + gk.gk_family = AF_INET6; + gk.gk_laddr6 = ip6->ip6_dst; + gk.gk_faddr6 = ip6->ip6_src; + + m = gre_ip_input(&gre_ip6_ops, m, *offp, ip6->ip6_hlim, &gk); + if (m == NULL) + return (IPPROTO_DONE); + + *mp = m; + return (rip6_input(mp, offp, proto, af)); +} +#endif /* INET6 */ + +/* + * generic GRE protocol handling + */ + +void +gre_init(void) +{ + pool_init(&gre_pcb_key_pool, sizeof(struct gre_pcb_key), 0, + IPL_NONE, PR_WAITOK, "grekey", NULL); + pool_init(&gre_pcb_pool, sizeof(struct gre_pcb), 0, + IPL_NONE, PR_WAITOK, "grepcb", NULL); +} + +int +gre_attach(struct socket *so, int proto) +{ + const struct gre_ops *ops = &gre_ip4_ops; + struct gre_pcb *gpcb; + struct inpcb *inp; + int error; + int flags = 0; + + if (so->so_pcb != NULL) + return (EINVAL); + + error = suser(curproc); + if (error != 0) + return (error); + + error = soreserve(so, gre_sendspace, gre_recvspace); + if (error != 0) + return (error); + +#ifdef INET6 + if (sotopf(so) == PF_INET6) { + ops = &gre_ip6_ops; + flags = INP_IPV6; + } +#endif + + gpcb = pool_get(&gre_pcb_pool, PR_NOWAIT | PR_ZERO); + if (gpcb == NULL) + return (ENOBUFS); + + inp = gpcb_inp(gpcb); + inp->inp_socket = so; + refcnt_init(&inp->inp_refcnt); + inp->inp_seclevel[SL_AUTH] = IPSEC_AUTH_LEVEL_DEFAULT; + inp->inp_seclevel[SL_ESP_TRANS] = IPSEC_ESP_TRANS_LEVEL_DEFAULT; + inp->inp_seclevel[SL_ESP_NETWORK] = IPSEC_ESP_NETWORK_LEVEL_DEFAULT; + inp->inp_seclevel[SL_IPCOMP] = IPSEC_IPCOMP_LEVEL_DEFAULT; + inp->inp_rtableid = curproc->p_p->ps_rtableid; + inp->inp_ip_minttl = 0; + inp->inp_flags |= flags; + inp->inp_hops = *ops->defttl; + inp->inp_ppcb = (caddr_t)gpcb; + + so->so_pcb = inp; + + return (0); +} + +static void +gre_inpdetach(struct gre_pcb *gpcb) +{ + struct socket *so = gpcb_so(gpcb); + struct inpcb *inp = gpcb_inp(gpcb); + + KASSERT(so->so_pcb == inp); + + so->so_pcb = NULL; + sofree(so, SL_NOUNLOCK); + + m_freem(inp->inp_options); + if (inp->inp_route.ro_rt) { + rtfree(inp->inp_route.ro_rt); + inp->inp_route.ro_rt = NULL; + } + + switch (ISSET(inp->inp_flags, INP_IPV6)) { + case 0: + ip_freemoptions(inp->inp_moptions); + break; +#ifdef INET6 + case INP_IPV6: + ip6_freepcbopts(inp->inp_outputopts6); + ip6_freemoptions(inp->inp_moptions6); + break; +#endif + } + + KASSERT((struct gre_pcb *)inp->inp_ppcb == (struct gre_pcb *)inp); + + (void)gre_disconnect(gpcb); + pool_put(&gre_pcb_pool, gpcb); +} + +int +gre_detach(struct socket *so) +{ + struct inpcb *inp; + + soassertlocked(so); + + inp = sotoinpcb(so); + if (inp == NULL) + return (EINVAL); + + gre_inpdetach(inp_gpcb(inp)); + + return (0); +} + +static int +gre_bind(const struct gre_ops *ops, struct gre_pcb *gpcb, struct mbuf *addr) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct socket *so = gpcb_so(gpcb); + struct sockaddr *sa; + unsigned int state = GRE_S_BOUND; + struct gre_pcb_key *gk, *ogk; + int reuse = ISSET(so->so_options, SO_REUSEPORT); + int error; + + if (gpcb->gpcb_pcb_key != NULL) + return (EISCONN); + + error = (*ops->op_nametosa)(inp, addr, &sa); + if (error != 0) + return (error); + + if ((*ops->op_is_wildcard)(sa)) { + state = GRE_S_WILDCARD; + } else if ((*ops->op_is_multicast)(sa)) { + /* + * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; + * allow complete duplication of binding if + * SO_REUSEPORT is set, or if SO_REUSEADDR is set + * and a multicast address is bound on both + * new and duplicated sockets. + */ + if (ISSET(so->so_options, SO_REUSEADDR|SO_REUSEPORT)) + reuse = SO_REUSEADDR|SO_REUSEPORT; + } else if (ISSET(so->so_options, SO_BINDANY) || + (*ops->op_is_broadcast)(inp->inp_rtableid, sa) == 0) { + /* + * we must check that we are binding to an address we + * own except when: + * - SO_BINDANY is set or + * - we are binding a UDP socket to 255.255.255.255 or + * - we are binding a UDP socket to one of our broadcast + * addresses + */ + ; + } else { + error = (*ops->op_is_local)(inp->inp_rtableid, sa); + if (error != 0) + return (error); + } + + gk = gre_pcb_key_get(gpcb); + if (gk == NULL) + return (ENOMEM); + + gk->gk_family = sa->sa_family; + (*ops->op_addr)(&gk->gk_laddr, sa); + gk->gk_proto = (*ops->op_proto)(sa); + + ogk = gre_pcb_key_insert(state, gk); + if (ogk != NULL) { + struct gre_pcb *ogpcb; + + gre_pcb_key_put(gk); + + ogpcb = gre_pcb_first(&ogk->gk_pcbs); + if (ogpcb != NULL) { + struct socket *oso = gpcb_so(ogpcb); + if (!ISSET(reuse, oso->so_options)) + return (EADDRINUSE); + } + + gk = ogk; + } + + /* commit */ + + gpcb->gpcb_pcb_key = gk; + gre_pcb_insert(&gk->gk_pcbs, gpcb); + + inp->inp_laddru = gk->gk_laddr; + inp->inp_lport = gk->gk_proto; + + gpcb->gpcb_reuse = reuse; + + return (0); +} + +static int +gre_connect(const struct gre_ops *ops, struct gre_pcb *gpcb, struct mbuf *addr) +{ + struct inpcb *inp = gpcb_inp(gpcb); + struct socket *so = gpcb_so(gpcb); + struct gre_pcb_key *bgk = gpcb->gpcb_pcb_key; + struct gre_pcb_key *gk, *ogk; + struct sockaddr *sa; + int reuse = ISSET(so->so_options, SO_REUSEPORT); + int error; + + if (bgk != NULL && bgk->gk_state == GRE_S_CONNECTED) + return (EISCONN); + + error = (*ops->op_nametosa)(inp, addr, &sa); + if (error != 0) + return (error); + + /* don't allow connections to wildcard addresses */ + if ((*ops->op_is_wildcard)(sa)) + return (EADDRNOTAVAIL); + + gk = gre_pcb_key_get(gpcb); + if (gk == NULL) + return (ENOBUFS); + + if (bgk == NULL || bgk->gk_state == GRE_S_WILDCARD) { + error = (*ops->op_selsrc)(inp, sa, &gk->gk_laddr, NULL); + if (error != 0) + goto put; + + gk->gk_proto = (*ops->op_proto)(sa); + } else { + if (bgk->gk_proto != (*ops->op_proto)(sa)) { + error = EADDRNOTAVAIL; + goto put; + } + + gk->gk_laddr = bgk->gk_laddr; + gk->gk_proto = bgk->gk_proto; + reuse |= gpcb->gpcb_reuse; + } + + gk->gk_family = sa->sa_family; + (*ops->op_addr)(&gk->gk_faddr, sa); + + ogk = gre_pcb_key_insert(GRE_S_CONNECTED, gk); + if (ogk != NULL) { + struct gre_pcb *ogpcb; + + gre_pcb_key_put(gk); + + ogpcb = gre_pcb_first(&ogk->gk_pcbs); + if (ogpcb != NULL) { + struct socket *oso = gpcb_so(ogpcb); + KASSERTMSG(oso != NULL, "ogk %p ogpcb %p oso %p", + ogk, ogpcb, oso); + if (!ISSET(reuse, oso->so_options)) + return (EADDRINUSE); + } + + gk = ogk; + } + + /* commit */ + + gre_disconnect(gpcb); + gpcb->gpcb_pcb_key = gk; + gre_pcb_insert(&gk->gk_pcbs, gpcb); + + inp->inp_laddru = gk->gk_laddr; + inp->inp_faddru = gk->gk_faddr; + inp->inp_lport = inp->inp_fport = gk->gk_proto; + + gpcb->gpcb_reuse = reuse; + soisconnected(so); + return (0); + +put: + gre_pcb_key_put(gk); + return (error); +} + + +static int +gre_getsockname(const struct gre_ops *ops, struct gre_pcb *gpcb, + struct mbuf *addr) +{ + struct gre_pcb_key *gk = gpcb->gpcb_pcb_key; + + if (gk == NULL) + return (ENOTCONN); + + (*ops->op_getsockname)(gpcb_inp(gpcb), addr); + + return (0); +} + +static int +gre_getpeername(const struct gre_ops *ops, struct gre_pcb *gpcb, + struct mbuf *addr) +{ + struct gre_pcb_key *gk = gpcb->gpcb_pcb_key; + + if (gk == NULL || gk->gk_state != GRE_S_CONNECTED) + return (ENOTCONN); + + (*ops->op_getpeername)(gpcb_inp(gpcb), addr); + + return (0); +} + +static int +gre_disconnect(struct gre_pcb *gpcb) +{ + struct gre_pcb_key *gk; + + gk = gpcb->gpcb_pcb_key; + if (gk == NULL) { + return (ENOTCONN); + } + + gre_pcb_remove(&gk->gk_pcbs, gpcb); + if (gre_pcb_empty(&gk->gk_pcbs)) { + switch (gk->gk_state) { + case GRE_S_WILDCARD: + RBT_REMOVE(gre_tree_wildcards, &gre_wildcards, gk); + break; + case GRE_S_BOUND: + RBT_REMOVE(gre_tree_bound, &gre_bound, gk); + break; + case GRE_S_CONNECTED: + RBT_REMOVE(gre_tree_connected, &gre_connected, gk); + break; + } + pool_put(&gre_pcb_key_pool, gk); + } + + gpcb->gpcb_pcb_key = NULL; + + return (0); +} + +static int +gre_usrreq(const struct gre_ops *ops, struct socket *so, int req, + struct mbuf *m, struct mbuf *addr, struct mbuf *control, struct proc *p) +{ + struct inpcb *inp; + struct gre_pcb *gpcb; + int error = 0; + + if (req == PRU_CONTROL) { + return ((*ops->op_control)(so, (u_long)m, (caddr_t)addr, + (struct ifnet *)control)); + } + + soassertlocked(so); + + inp = sotoinpcb(so); + if (inp == NULL) { + error = EINVAL; + goto release; + } + gpcb = inp_gpcb(inp); + + switch (req) { + case PRU_BIND: + error = gre_bind(ops, gpcb, addr); + break; + + case PRU_LISTEN: + error = EOPNOTSUPP; + break; + + case PRU_CONNECT: + error = gre_connect(ops, gpcb, addr); + break; + + case PRU_CONNECT2: + error = EOPNOTSUPP; + break; + + case PRU_ACCEPT: + error = EOPNOTSUPP; + break; + + case PRU_DISCONNECT: + error = gre_disconnect(gpcb); + if (error != 0) + break; + + gpcb->gpcb_reuse = 0; + CLR(so->so_state, SS_ISCONNECTED); /* XXX cos udp_usrreq.c */ + memset(&inp->inp_laddru, 0, sizeof(inp->inp_laddru)); + memset(&inp->inp_faddru, 0, sizeof(inp->inp_faddru)); + inp->inp_lport = inp->inp_fport = 0; + break; + + case PRU_SHUTDOWN: + socantsendmore(so); + break; + + case PRU_SEND: + return (gre_send(ops, gpcb, m, addr, control)); + + case PRU_ABORT: + soisdisconnected(so); + gre_inpdetach(gpcb); + break; + + case PRU_SOCKADDR: + return (gre_getsockname(ops, gpcb, addr)); + break; + case PRU_PEERADDR: + return (gre_getpeername(ops, gpcb, addr)); + break; + + case PRU_SENSE: + /* stat: don't bother with a block size. */ + break; + + case PRU_SENDOOB: + case PRU_FASTTIMO: + case PRU_SLOWTIMO: + case PRU_PROTORCV: + case PRU_PROTOSEND: + case PRU_RCVD: + case PRU_RCVOOB: + error = EOPNOTSUPP; + break; + + default: + panic("%s req %d", __func__, req); + } + +release: + switch (req) { + case PRU_RCVD: + case PRU_RCVOOB: + case PRU_SENSE: + break; + default: + m_freem(control); + m_freem(m); + break; + } + + return (error); +} + +static int +gre_setopt(struct gre_pcb *gpcb, int optname, struct mbuf *m) +{ + /* + * only support changing these options when the socket is + * completely disconnected. the amount of code needed to try + * changing the gre_pcb_key was "quite large" and arguably + * not worth it. + */ + switch (optname) { + case GRE_CKSUM: + case GRE_KEY: + case GRE_SEQ: + if (gpcb->gpcb_pcb_key != NULL) + return (EISCONN); + break; + } + + switch (optname) { + case GRE_CKSUM: + if (m == NULL || m->m_len != sizeof(int)) + return (EINVAL); + + if (*mtod(m, int *)) + SET(gpcb->gpcb_flags, htons(GRE_CP)); + else + CLR(gpcb->gpcb_flags, htons(GRE_CP)); + break; + case GRE_KEY: + if (m == NULL || m->m_len == 0) { /* disable key */ + CLR(gpcb->gpcb_flags, htons(GRE_KP)); + gpcb->gpcb_key = htonl(0); + break; + } + + if (m->m_len != sizeof(gpcb->gpcb_key)) + return (EINVAL); + + SET(gpcb->gpcb_flags, htons(GRE_KP)); + htobem32(&gpcb->gpcb_key, *mtod(m, uint32_t *)); + break; + case GRE_SEQ: + if (m == NULL || m->m_len == 0) { /* disable seq */ + CLR(gpcb->gpcb_flags, htons(GRE_SP)); + gpcb->gpcb_seq = 0; + break; + } + + if (m->m_len != sizeof(gpcb->gpcb_seq)) + return (EINVAL); + + SET(gpcb->gpcb_flags, htons(GRE_SP)); + gpcb->gpcb_seq = *mtod(m, uint32_t *); + break; + case GRE_RECVSEQ: + if (m == NULL || m->m_len != sizeof(int)) + return (EINVAL); + + if (*mtod(m, int *)) + SET(gpcb->gpcb_pflags, GREPCB_RECVSEQ); + else + CLR(gpcb->gpcb_pflags, GREPCB_RECVSEQ); + break; + default: + return (ENOPROTOOPT); + break; + } + + return (0); +} + +static int +gre_getopt(struct gre_pcb *gpcb, int optname, struct mbuf *m) +{ + switch (optname) { + case GRE_KEY: + if (!ISSET(gpcb->gpcb_flags, htons(GRE_KP))) + return (ENOTCONN); + + m->m_len = sizeof(gpcb->gpcb_key); + *mtod(m, uint32_t *) = bemtoh32(&gpcb->gpcb_key); + break; + case GRE_CKSUM: + m->m_len = sizeof(int); + *mtod(m, int *) = !!ISSET(gpcb->gpcb_flags, htons(GRE_CP)); + break; + case GRE_SEQ: + if (!ISSET(gpcb->gpcb_flags, htons(GRE_SP))) + return (ENOTCONN); + + m->m_len = sizeof(gpcb->gpcb_seq); + *mtod(m, uint32_t *) = gpcb->gpcb_key; + break; + default: + return (ENOPROTOOPT); + } + + return (0); +} + +/* + * IP socket option processing. + */ +static int +gre_ctloutput(const struct gre_ops *ops, int op, struct socket *so, + int level, int optname, struct mbuf *m) +{ + struct inpcb *inp; + struct gre_pcb *gpcb; + int error; + + inp = sotoinpcb(so); + if (inp == NULL) + return (ECONNRESET); + if (level != IPPROTO_GRE) + return ((*ops->op_ctloutput)(level, so, level, optname, m)); + + gpcb = inp_gpcb(inp); + + switch (op) { + case PRCO_SETOPT: + error = gre_setopt(gpcb, optname, m); + break; + case PRCO_GETOPT: + error = gre_getopt(gpcb, optname, m); + break; + default: + panic("%s op %d", __func__, op); + } + + return (error); +} + +static void * +gre_pullup(struct mbuf **mp, int *offp, int len) +{ + int hlen = *offp + len; + void *h; + + if ((*mp)->m_pkthdr.len < hlen) + return (NULL); /* decline */ + + *mp = m_pullup(*mp, hlen); + if (*mp == NULL) + return (NULL); + + h = mtod(*mp, caddr_t) + *offp; + *offp = hlen; + + return (h); +} + +static int +gre_candeliver(const struct gre_pcb *gpcb, uint8_t ttl) +{ + const struct inpcb *inp = gpcb_inp(gpcb); + + if (ISSET(inp->inp_socket->so_state, SS_CANTRCVMORE)) + return (0); + + if (inp->inp_ip_minttl > ttl) + return (0); + + return (1); +} + +static struct mbuf * +gre_ip_input(const struct gre_ops *ops, struct mbuf *m, int iphlen, + uint8_t ttl, struct gre_pcb_key *key) +{ + int hlen = iphlen; + struct gre_header *gh; + struct gre_pcb_key *gk; + struct gre_pcb *gpcb, *ngpcb; + uint16_t cksum = 0; + uint32_t seqno = 0; + + gh = gre_pullup(&m, &hlen, sizeof(*gh)); + if (gh == NULL) + return (m); + + if ((gh->gre_flags & htons(GRE_VERS_MASK)) != htons(GRE_VERS_0)) + return (m); /* decline */ + + if (ISSET(gh->gre_flags, ~htons(GRE_VALID_MASK))) + return (m); /* decline */ + + key->gk_flags = gh->gre_flags; + key->gk_proto = gh->gre_proto; + + if (ISSET(key->gk_flags, htons(GRE_CP))) { + struct gre_h_cksum *gch; + + gch = gre_pullup(&m, &hlen, sizeof(*gch)); + if (gch == NULL) + return (m); + + cksum = gch->gre_cksum; + + /* XXX ignore Reserved (Offset) field */ + } + + if (ISSET(key->gk_flags, htons(GRE_KP))) { + struct gre_h_key *gkh; + + gkh = gre_pullup(&m, &hlen, sizeof(*gkh)); + if (gkh == NULL) + return (m); + + key->gk_key = gkh->gre_key; + } + + if (ISSET(key->gk_flags, htons(GRE_SP))) { + struct gre_h_seq *gsh; + + gsh = gre_pullup(&m, &hlen, sizeof(*gsh)); + if (gsh == NULL) + return (m); + + seqno = bemtoh32(&gsh->gre_seq); + } + + key->gk_rtableid = m->m_pkthdr.ph_rtableid; + + gk = RBT_FIND(gre_tree_connected, &gre_connected, key); + if (gk == NULL) { + gk = RBT_FIND(gre_tree_bound, &gre_bound, key); + if (gk == NULL) { + gk = RBT_FIND(gre_tree_wildcards, &gre_wildcards, key); + if (gk == NULL) + return (m); /* decline */ + } + } + + /* it's ours now */ + + if (ISSET(key->gk_flags, htons(GRE_CP))) { + /* XXX actually do the checksum calc */ + } + + gpcb = gre_pcb_first(&gk->gk_pcbs); + while (!gre_candeliver(gpcb, ttl)) { + gpcb = gre_pcb_next(gpcb); + if (gpcb == NULL) + goto drop; + } + + ngpcb = gpcb; + for (;;) { + struct mbuf *mm; + + ngpcb = gre_pcb_next(ngpcb); + if (ngpcb == NULL) + break; + + if (!gre_candeliver(ngpcb, ttl)) + continue; + + mm = m_dup_pkt(m, 0, M_DONTWAIT); + if (mm == NULL) { + /* assume further copies will also fail */ + break; + } + + (*ops->op_sbappend)(gpcb, mm, hlen, seqno); + gpcb = ngpcb; + } + + (*ops->op_sbappend)(gpcb, m, hlen, seqno); + + return (NULL); + +drop: + m_freem(m); + return (NULL); +} + +static int +gre_pcb_key_cmp_wildcard(const struct gre_pcb_key *a, + const struct gre_pcb_key *b) +{ + if (a->gk_proto > b->gk_proto) + return (1); + if (a->gk_proto < b->gk_proto) + return (-1); + + if (a->gk_flags > b->gk_flags) + return (1); + if (a->gk_flags < b->gk_flags) + return (-1); + + if (ISSET(a->gk_flags, htons(GRE_KP))) { + if (a->gk_key > b->gk_key) + return (1); + if (a->gk_key < b->gk_key) + return (-1); + } + + if (a->gk_rtableid > b->gk_rtableid) + return (1); + if (a->gk_rtableid < b->gk_rtableid) + return (-1); + + if (a->gk_family > b->gk_family) + return (1); + if (a->gk_family < b->gk_family) + return (-1); + + return (0); +} + +RBT_GENERATE(gre_tree_wildcards, gre_pcb_key, gk_entry, + gre_pcb_key_cmp_wildcard); + +static int +gre_pcb_key_cmp_bound(const struct gre_pcb_key *a, + const struct gre_pcb_key *b) +{ + int rv; + + rv = gre_pcb_key_cmp_wildcard(a, b); + if (rv != 0) + return (rv); + + switch (a->gk_family) { + case AF_INET: + rv = memcmp(&a->gk_laddr4, &b->gk_laddr4, sizeof(a->gk_laddr4)); + break; +#ifdef INET + case AF_INET6: + rv = memcmp(&a->gk_laddr6, &b->gk_laddr6, sizeof(a->gk_laddr6)); + break; +#endif + default: + unhandled_af(a->gk_family); + } + + return (rv); +} + +RBT_GENERATE(gre_tree_bound, gre_pcb_key, gk_entry, gre_pcb_key_cmp_bound); + +static int +gre_pcb_key_cmp_connected(const struct gre_pcb_key *a, + const struct gre_pcb_key *b) +{ + int rv; + + rv = gre_pcb_key_cmp_bound(a, b); + if (rv != 0) + return (rv); + + switch (a->gk_family) { + case AF_INET: + rv = memcmp(&a->gk_faddr4, &b->gk_faddr4, sizeof(a->gk_faddr4)); + break; +#ifdef INET + case AF_INET6: + rv = memcmp(&a->gk_faddr6, &b->gk_faddr6, sizeof(a->gk_faddr6)); + break; +#endif + default: + unhandled_af(a->gk_family); + } + + return (rv); +} + +RBT_GENERATE(gre_tree_connected, gre_pcb_key, gk_entry, + gre_pcb_key_cmp_connected); Index: sys/netinet6/in6_proto.c =================================================================== RCS file: /cvs/src/sys/netinet6/in6_proto.c,v retrieving revision 1.104 diff -u -p -r1.104 in6_proto.c --- sys/netinet6/in6_proto.c 13 Jun 2019 08:12:11 -0000 1.104 +++ sys/netinet6/in6_proto.c 29 Oct 2019 07:57:58 -0000 @@ -117,7 +117,7 @@ #include "gre.h" #if NGRE > 0 -#include +#include #endif /* @@ -340,11 +340,22 @@ const struct protosw inet6sw[] = { .pr_domain = &inet6domain, .pr_protocol = IPPROTO_GRE, .pr_flags = PR_ATOMIC|PR_ADDR, - .pr_input = gre_input6, + .pr_input = gre_ip6_input, .pr_ctloutput = rip6_ctloutput, .pr_usrreq = rip6_usrreq, .pr_attach = rip6_attach, .pr_detach = rip6_detach, +}, +{ + .pr_type = SOCK_DGRAM, + .pr_domain = &inet6domain, + .pr_protocol = IPPROTO_GRE, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = gre_ip6_input, + .pr_ctloutput = gre_ip6_ctloutput, + .pr_usrreq = gre_ip6_usrreq, + .pr_attach = gre_attach, + .pr_detach = gre_detach, }, #endif /* NGRE */