-int
-udp6_output(in6p, m, addr6, control, p)
- register struct inpcb *in6p;
- register struct mbuf *m;
- struct sockaddr *addr6;
- struct mbuf *control;
- struct proc *p;
-{
- register int ulen = m->m_pkthdr.len;
- int plen = sizeof(struct udphdr) + ulen;
- struct ip6_hdr *ip6;
- struct udphdr *udp6;
- struct in6_addr laddr6;
- int s = 0, error = 0;
- struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts;
- int flags;
-
- if (control) {
- if (error = ip6_setpktoptions(control, &opt,
- p &&
- suser(p->p_ucred, &p->p_acflag), 0))
- goto release;
- in6p->in6p_outputopts = &opt;
- }
-
- if (addr6) {
- laddr6 = in6p->in6p_laddr;
- if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
- error = EISCONN;
- goto release;
- }
- /*
- * Must block input while temporarily connected.
- */
- s = splnet();
- /*
- * XXX: the user might want to overwrite the local address
- * via an ancillary data.
- */
- bzero(&in6p->in6p_laddr, sizeof(struct in6_addr));
- error = in6_pcbconnect(in6p, addr6, p);
- if (error) {
- splx(s);
- goto release;
- }
- } else {
- if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) {
- error = ENOTCONN;
- goto release;
- }
- }
- /*
- * Calculate data length and get a mbuf
- * for UDP and IP6 headers.
- */
- M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct udphdr),
- M_DONTWAIT);
- if (m == 0) {
- error = ENOBUFS;
- if (addr6)
- splx(s);
- goto release;
- }
-
- /*
- * Stuff checksum and output datagram.
- */
- ip6 = mtod(m, struct ip6_hdr *);
- ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK;
- ip6->ip6_vfc |= IPV6_VERSION;
-#if 0 /* ip6_plen will be filled in ip6_output. */
- ip6->ip6_plen = htons((u_short)plen);
-#endif
- ip6->ip6_nxt = IPPROTO_UDP;
- ip6->ip6_hlim = in6_selecthlim(in6p,
- in6p->in6p_route.ro_rt ?
- in6p->in6p_route.ro_rt->rt_ifp :
- NULL);
- ip6->ip6_src = in6p->in6p_laddr;
- ip6->ip6_dst = in6p->in6p_faddr;
-
- udp6 = (struct udphdr *)(ip6 + 1);
- udp6->uh_sport = in6p->in6p_lport;
- udp6->uh_dport = in6p->in6p_fport;
- udp6->uh_ulen = htons((u_short)plen);
- udp6->uh_sum = 0;
-
- if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP,
- sizeof(struct ip6_hdr), plen)) == 0) {
- udp6->uh_sum = 0xffff;
- }
-
- flags = 0;
- if (in6p->in6p_flags & IN6P_MINMTU)
- flags |= IPV6_MINMTU;
-
- udpstat.udps_opackets++;
-
-#if IPSEC
- ipsec_setsocket(m, in6p->in6p_socket);
-#endif /*IPSEC*/
- error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route,
- flags, in6p->in6p_moptions, NULL);
-
- if (addr6) {
- in6_pcbdisconnect(in6p);
- in6p->in6p_laddr = laddr6;
- splx(s);
- }
- goto releaseopt;
-
-release:
- m_freem(m);
-
-releaseopt:
- if (control) {
- ip6_clearpktopts(in6p->in6p_outputopts, 0, -1);
- in6p->in6p_outputopts = stickyopt;
- m_freem(control);
- }
- return(error);
-}