static struct mbuf * m_getm(struct mbuf *m, int len, int how, int type) { struct mbuf *top, *tail, *mp, *mtail = NULL; KASSERT(len >= 0); mp = m_get(how, type); if (mp == NULL) return (NULL); else if (len > MINCLSIZE) { m_clget(mp, how); if ((mp->m_flags & M_EXT) == 0) { m_free(mp); return (NULL); } } mp->m_len = 0; len -= M_TRAILINGSPACE(mp); if (m != NULL) for (mtail = m; mtail->m_next != NULL; mtail = mtail->m_next); else m = mp; top = tail = mp; while (len > 0) { mp = m_get(how, type); if (mp == NULL) goto failed; tail->m_next = mp; tail = mp; if (len > MINCLSIZE) { m_clget(mp, how); if ((mp->m_flags & M_EXT) == 0) goto failed; } mp->m_len = 0; len -= M_TRAILINGSPACE(mp); } if (mtail != NULL) mtail->m_next = top; return (m); failed: m_freem(top); return (NULL); }
int rump_netconfig_auto_ipv6(const char *ifname) { struct ifnet *ifp; int ifindex; struct socket *rsso = NULL; int rv = 0; int hoplimit = 255; struct mbuf *m_nam = NULL, *m_outbuf = NULL; struct sockaddr_in6 *sin6; char *buf; struct nd_router_solicit rs; struct nd_opt_hdr opt; ifp = ifunit(ifname); if (ifp == NULL) { rv = ENXIO; goto out; } if (ifp->if_sadl->sdl_type != IFT_ETHER) { rv = EINVAL; goto out; } rv = socreate(PF_INET6, &rsso, SOCK_RAW, IPPROTO_ICMPV6, curlwp, NULL); if (rv != 0) goto out; ifindex = ifp->if_index; rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof ifindex); if (rv != 0) goto out; rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hoplimit, sizeof hoplimit); if (rv != 0) goto out; m_nam = m_get(M_WAIT, MT_SONAME); sin6 = mtod(m_nam, struct sockaddr_in6 *); sin6->sin6_len = m_nam->m_len = sizeof (*sin6); sin6->sin6_family = AF_INET6; netconfig_inet_pton6("ff02::2", &sin6->sin6_addr); #define rslen (sizeof rs + sizeof opt + ETHER_ADDR_LEN) CTASSERT(rslen <= MCLBYTES); m_outbuf = m_gethdr(M_WAIT, MT_DATA); m_clget(m_outbuf, M_WAIT); m_outbuf->m_pkthdr.len = m_outbuf->m_len = rslen; #if __NetBSD_Prereq__(7,99,31) m_set_rcvif(m_outbuf, NULL); #else m_outbuf->m_pkthdr.rcvif = NULL; #endif #undef rslen buf = mtod(m_outbuf, char *); memset(&rs, 0, sizeof rs); rs.nd_rs_type = ND_ROUTER_SOLICIT; memset(&opt, 0, sizeof opt); opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; opt.nd_opt_len = 1; /* units of 8 octets */ memcpy(buf, &rs, sizeof rs); buf += sizeof rs; memcpy(buf, &opt, sizeof opt); buf += sizeof opt; memcpy(buf, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN); ip6_accept_rtadv = 1; rv = rump_netconfig_ifup(ifname); if (rv != 0) goto out; #if __NetBSD_Prereq__(7,99,12) rv = (*rsso->so_send)(rsso, (struct sockaddr *)sin6, NULL, m_outbuf, NULL, 0, curlwp); #else rv = (*rsso->so_send)(rsso, m_nam, NULL, m_outbuf, NULL, 0, curlwp); #endif if (rv == 0) /* *(so_send)() takes ownership of m_outbuf on success */ m_outbuf = NULL; else goto out; rv = 0; out: if (m_nam) m_freem(m_nam); if (m_outbuf) m_freem(m_outbuf); if (rsso) soclose(rsso); return rv; }
/* * Slightly changed version of sosend() */ static int kttcp_sosend(struct socket *so, unsigned long long slen, unsigned long long *done, struct lwp *l, int flags) { struct mbuf **mp, *m, *top; long space, len, mlen; int error, dontroute, atomic; long long resid; atomic = sosendallatonce(so); resid = slen; top = NULL; /* * In theory resid should be unsigned. * However, space must be signed, as it might be less than 0 * if we over-committed, and we must use a signed comparison * of space and resid. On the other hand, a negative resid * causes us to loop sending 0-length segments to the protocol. */ if (resid < 0) { error = EINVAL; goto out; } dontroute = (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && (so->so_proto->pr_flags & PR_ATOMIC); l->l_ru.ru_msgsnd++; #define snderr(errno) { error = errno; goto release; } solock(so); restart: if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0) goto out; do { if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); if (so->so_error) { error = so->so_error; so->so_error = 0; goto release; } if ((so->so_state & SS_ISCONNECTED) == 0) { if (so->so_proto->pr_flags & PR_CONNREQUIRED) { snderr(ENOTCONN); } else { snderr(EDESTADDRREQ); } } space = sbspace(&so->so_snd); if (flags & MSG_OOB) space += 1024; if ((atomic && resid > so->so_snd.sb_hiwat)) snderr(EMSGSIZE); if (space < resid && (atomic || space < so->so_snd.sb_lowat)) { if (so->so_state & SS_NBIO) snderr(EWOULDBLOCK); SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive sbwait 1"); SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive sbwait 1"); sbunlock(&so->so_snd); error = sbwait(&so->so_snd); if (error) goto out; goto restart; } mp = ⊤ do { sounlock(so); do { if (top == 0) { m = m_gethdr(M_WAIT, MT_DATA); mlen = MHLEN; m->m_pkthdr.len = 0; m->m_pkthdr.rcvif = NULL; } else { m = m_get(M_WAIT, MT_DATA); mlen = MLEN; } if (resid >= MINCLSIZE && space >= MCLBYTES) { m_clget(m, M_WAIT); if ((m->m_flags & M_EXT) == 0) goto nopages; mlen = MCLBYTES; #ifdef MAPPED_MBUFS len = lmin(MCLBYTES, resid); #else if (atomic && top == 0) { len = lmin(MCLBYTES - max_hdr, resid); m->m_data += max_hdr; } else len = lmin(MCLBYTES, resid); #endif space -= len; } else { nopages: len = lmin(lmin(mlen, resid), space); space -= len; /* * For datagram protocols, leave room * for protocol headers in first mbuf. */ if (atomic && top == 0 && len < mlen) MH_ALIGN(m, len); } resid -= len; m->m_len = len; *mp = m; top->m_pkthdr.len += len; if (error) goto release; mp = &m->m_next; if (resid <= 0) { if (flags & MSG_EOR) top->m_flags |= M_EOR; break; } } while (space > 0 && atomic); solock(so); if (so->so_state & SS_CANTSENDMORE) snderr(EPIPE); if (dontroute) so->so_options |= SO_DONTROUTE; if (resid > 0) so->so_state |= SS_MORETOCOME; if (flags & MSG_OOB) error = (*so->so_proto->pr_usrreqs->pr_sendoob)(so, top, NULL); else error = (*so->so_proto->pr_usrreqs->pr_send)(so, top, NULL, NULL, l); if (dontroute) so->so_options &= ~SO_DONTROUTE; if (resid > 0) so->so_state &= ~SS_MORETOCOME; top = 0; mp = ⊤ if (error) goto release; } while (resid && space > 0); } while (resid); release: sbunlock(&so->so_snd); out: sounlock(so); if (top) m_freem(top); *done = slen - resid; #if 0 printf("sosend: error %d slen %llu resid %lld\n", error, slen, resid); #endif return (error); }
static int bpf_movein(struct uio *uio, int linktype, uint64_t mtu, struct mbuf **mp, struct sockaddr *sockp) { struct mbuf *m; int error; size_t len; size_t hlen; size_t align; /* * Build a sockaddr based on the data link layer type. * We do this at this level because the ethernet header * is copied directly into the data field of the sockaddr. * In the case of SLIP, there is no header and the packet * is forwarded as is. * Also, we are careful to leave room at the front of the mbuf * for the link level header. */ switch (linktype) { case DLT_SLIP: sockp->sa_family = AF_INET; hlen = 0; align = 0; break; case DLT_PPP: sockp->sa_family = AF_UNSPEC; hlen = 0; align = 0; break; case DLT_EN10MB: sockp->sa_family = AF_UNSPEC; /* XXX Would MAXLINKHDR be better? */ /* 6(dst)+6(src)+2(type) */ hlen = sizeof(struct ether_header); align = 2; break; case DLT_ARCNET: sockp->sa_family = AF_UNSPEC; hlen = ARC_HDRLEN; align = 5; break; case DLT_FDDI: sockp->sa_family = AF_LINK; /* XXX 4(FORMAC)+6(dst)+6(src) */ hlen = 16; align = 0; break; case DLT_ECONET: sockp->sa_family = AF_UNSPEC; hlen = 6; align = 2; break; case DLT_NULL: sockp->sa_family = AF_UNSPEC; hlen = 0; align = 0; break; default: return (EIO); } len = uio->uio_resid; /* * If there aren't enough bytes for a link level header or the * packet length exceeds the interface mtu, return an error. */ if (len - hlen > mtu) return (EMSGSIZE); /* * XXX Avoid complicated buffer chaining --- * bail if it won't fit in a single mbuf. * (Take into account possible alignment bytes) */ if (len + align > MCLBYTES) return (EIO); m = m_gethdr(M_WAIT, MT_DATA); m_reset_rcvif(m); m->m_pkthdr.len = (int)(len - hlen); if (len + align > MHLEN) { m_clget(m, M_WAIT); if ((m->m_flags & M_EXT) == 0) { error = ENOBUFS; goto bad; } } /* Insure the data is properly aligned */ if (align > 0) { m->m_data += align; m->m_len -= (int)align; } error = uiomove(mtod(m, void *), len, uio); if (error) goto bad; if (hlen != 0) { memcpy(sockp->sa_data, mtod(m, void *), hlen); m->m_data += hlen; /* XXX */ len -= hlen; }