📄 ip_output.c
字号:
ip->ip_len = htons((u_short) ip->ip_len);
ip->ip_off = htons((u_short) ip->ip_off);
ip->ip_sum = 0;
/*
* Now we check if this tdb has all the transforms which
* are requried by the socket or our default policy.
*/
SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb);
if (sa_require & ~sa_have)
goto no_encap;
if (tdb == NULL) {
splx(s);
if (gw->sen_type == SENT_IPSP)
DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), gw->sen_ipsp_sproto));
#ifdef INET6
else
DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet6_ntoa4(gw->sen_ipsp6_dst), ntohl(gw->sen_ipsp6_spi), gw->sen_ipsp6_sproto));
#endif /* INET6 */
if (re->re_rt)
RTFREE(re->re_rt);
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
for (t = tdb; t != NULL; t = t->tdb_onext)
if ((t->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
(t->tdb_sproto == IPPROTO_AH && !ah_enable)) {
DPRINTF(("ip_output(): IPSec outbound packet dropped due to policy\n"));
if (re->re_rt)
RTFREE(re->re_rt);
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
while (tdb && tdb->tdb_xform) {
/* Check if the SPI is invalid */
if (tdb->tdb_flags & TDBF_INVALID) {
splx(s);
DPRINTF(("ip_output(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto));
m_freem(m);
if (re->re_rt)
RTFREE(re->re_rt);
return ENXIO;
}
#ifndef INET6
/* Sanity check */
if (tdb->tdb_dst.sa.sa_family != AF_INET) {
splx(s);
DPRINTF(("ip_output(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family));
m_freem(m);
if (re->re_rt)
RTFREE(re->re_rt);
return ENXIO;
}
#endif /* INET6 */
/* Register first use, setup expiration timer */
if (tdb->tdb_first_use == 0) {
tdb->tdb_first_use = time.tv_sec;
tdb_expiration(tdb, TDBEXP_TIMEOUT);
}
/* Check for tunneling */
if (((tdb->tdb_dst.sa.sa_family == AF_INET) &&
(tdb->tdb_dst.sin.sin_addr.s_addr !=
INADDR_ANY) &&
(tdb->tdb_dst.sin.sin_addr.s_addr !=
ip->ip_dst.s_addr)) ||
(tdb->tdb_dst.sa.sa_family == AF_INET6) ||
((tdb->tdb_flags & TDBF_TUNNELING) &&
(tdb->tdb_xform->xf_type != XF_IP4))) {
/* Fix length and checksum */
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
error = ipe4_output(m, tdb, &mp,
ip->ip_hl << 2,
offsetof(struct ip, ip_p));
if (mp == NULL)
error = EFAULT;
if (error) {
splx(s);
if (re->re_rt)
RTFREE(re->re_rt);
return error;
}
if (tdb->tdb_dst.sa.sa_family == AF_INET)
ip6flag = 0;
#ifdef INET6
if (tdb->tdb_dst.sa.sa_family == AF_INET6)
ip6flag = 1;
#endif /* INET6 */
m = mp;
mp = NULL;
}
if ((tdb->tdb_xform->xf_type == XF_IP4) &&
(tdb->tdb_dst.sa.sa_family == AF_INET)) {
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
}
#ifdef INET6
if ((tdb->tdb_xform->xf_type == XF_IP4) &&
(tdb->tdb_dst.sa.sa_family == AF_INET6)) {
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len);
}
#endif /* INET6 */
#ifdef INET6
/*
* This assumes that there is only just an IPv6
* header prepended.
*/
if (ip6flag)
error = (*(tdb->tdb_xform->xf_output))(m, tdb, &mp, sizeof(struct ip6_hdr), offsetof(struct ip6_hdr, ip6_nxt));
#endif /* INET6 */
if (!ip6flag)
error = (*(tdb->tdb_xform->xf_output))(m, tdb, &mp, ip->ip_hl << 2, offsetof(struct ip, ip_p));
if (!error && mp == NULL)
error = EFAULT;
if (error) {
splx(s);
if (mp != NULL)
m_freem(mp);
if (re->re_rt)
RTFREE(re->re_rt);
return error;
}
m = mp;
mp = NULL;
if (!ip6flag) {
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
}
#ifdef INET6
if (ip6flag) {
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len);
}
#endif /* INET6 */
tdb = tdb->tdb_onext;
}
splx(s);
if (!ip6flag)
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
/*
* At this point, m is pointing to an mbuf chain with the
* processed packet. Call ourselves recursively, but
* bypass the encap code.
*/
if (re->re_rt)
RTFREE(re->re_rt);
if (!ip6flag) {
ip = mtod(m, struct ip *);
NTOHS(ip->ip_len);
NTOHS(ip->ip_off);
return ip_output(m, NULL, NULL,
IP_ENCAPSULATED | IP_RAWOUTPUT,
NULL, NULL);
}
#ifdef INET6
if (ip6flag) {
ip6 = mtod(m, struct ip6_hdr *);
NTOHS(ip6->ip6_plen);
/* Naturally, ip6_output() has to honor those two flags */
return ip6_output(m, NULL, NULL,
IP_ENCAPSULATED | IP_RAWOUTPUT,
NULL, NULL);
}
#endif /* INET6 */
no_encap:
/* This is for possible future use, don't move or delete */
if (re->re_rt)
RTFREE(re->re_rt);
/* No IPSec processing though it was required, drop packet */
if (sa_require) {
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
}
#endif /* IPSEC */
#if defined(IPFILTER) || defined(IPFILTER_LKM)
/*
* looks like most checking has been done now...do a filter check
*/
{
struct mbuf *m0 = m;
if (fr_checkp && (*fr_checkp)(ip, hlen, ifp, 1, &m0)) {
error = EHOSTUNREACH;
goto done;
} else
ip = mtod(m = m0, struct ip *);
}
#endif
/*
* If small enough for interface, can just send directly.
*/
if ((u_int16_t)ip->ip_len <= ifp->if_mtu) {
ip->ip_len = htons((u_int16_t)ip->ip_len);
ip->ip_off = htons((u_int16_t)ip->ip_off);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, hlen);
error = (*ifp->if_output)(ifp, m, sintosa(dst), ro->ro_rt);
goto done;
}
/*
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
#if 0
/*
* If IPsec packet is too big for the interface, try fragment it.
* XXX This really is a quickhack. May be inappropriate.
* XXX fails if somebody is sending AH'ed packet, with:
* sizeof(packet without AH) < mtu < sizeof(packet with AH)
*/
if (sab && ip->ip_p != IPPROTO_AH && (flags & IP_FORWARDING) == 0)
ip->ip_off &= ~IP_DF;
#endif /*IPSEC*/
if (ip->ip_off & IP_DF) {
error = EMSGSIZE;
ipstat.ips_cantfrag++;
goto bad;
}
len = (ifp->if_mtu - hlen) &~ 7;
if (len < 8) {
error = EMSGSIZE;
goto bad;
}
{
int mhlen, firstlen = len;
struct mbuf **mnext = &m->m_nextpkt;
/*
* Loop through length of segment after first fragment,
* make new header and copy data of each part and link onto chain.
*/
m0 = m;
mhlen = sizeof (struct ip);
for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (m == 0) {
error = ENOBUFS;
ipstat.ips_odropped++;
goto sendorfree;
}
*mnext = m;
mnext = &m->m_nextpkt;
m->m_data += max_linkhdr;
mhip = mtod(m, struct ip *);
*mhip = *ip;
if (hlen > sizeof (struct ip)) {
mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
mhip->ip_hl = mhlen >> 2;
}
m->m_len = mhlen;
mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
if (ip->ip_off & IP_MF)
mhip->ip_off |= IP_MF;
if (off + len >= (u_int16_t)ip->ip_len)
len = (u_int16_t)ip->ip_len - off;
else
mhip->ip_off |= IP_MF;
mhip->ip_len = htons((u_int16_t)(len + mhlen));
m->m_next = m_copy(m0, off, len);
if (m->m_next == 0) {
error = ENOBUFS; /* ??? */
ipstat.ips_odropped++;
goto sendorfree;
}
m->m_pkthdr.len = mhlen + len;
m->m_pkthdr.rcvif = (struct ifnet *)0;
mhip->ip_off = htons((u_int16_t)mhip->ip_off);
mhip->ip_sum = 0;
mhip->ip_sum = in_cksum(m, mhlen);
ipstat.ips_ofragments++;
}
/*
* Update first fragment by trimming what's been copied out
* and updating header, then send each fragment (in order).
*/
m = m0;
m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);
m->m_pkthdr.len = hlen + firstlen;
ip->ip_len = htons((u_int16_t)m->m_pkthdr.len);
ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, hlen);
sendorfree:
for (m = m0; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
if (error == 0)
error = (*ifp->if_output)(ifp, m, sintosa(dst),
ro->ro_rt);
else
m_freem(m);
}
if (error == 0)
ipstat.ips_fragmented++;
}
done:
if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt)
RTFREE(ro->ro_rt);
return (error);
bad:
m_freem(m0);
goto done;
}
/*
* Insert IP options into preformed packet.
* Adjust IP destination as required for IP source routing,
* as indicated by a non-zero in_addr at the start of the options.
*/
static struct mbuf *
ip_insertoptions(m, opt, phlen)
register struct mbuf *m;
struct mbuf *opt;
int *phlen;
{
register struct ipoption *p = mtod(opt, struct ipoption *);
struct mbuf *n;
register struct ip *ip = mtod(m, struct ip *);
unsigned optlen;
optlen = opt->m_len - sizeof(p->ipopt_dst);
if (optlen + (u_int16_t)ip->ip_len > IP_MAXPACKET)
return (m); /* XXX should fail */
if (p->ipopt_dst.s_addr)
ip->ip_dst = p->ipopt_dst;
if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) {
MGETHDR(n, M_DONTWAIT, MT_HEADER);
if (n == 0)
return (m);
n->m_pkthdr.len = m->m_pkthdr.len + optlen;
m->m_len -= sizeof(struct ip);
m->m_data += sizeof(struct ip);
n->m_next = m;
m = n;
m->m_len = optlen + sizeof(struct ip);
m->m_data += max_linkhdr;
bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
} else {
m->m_data -= optlen;
m->m_len += optlen;
m->m_pkthdr.len += optlen;
ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
}
ip = mtod(m, struct ip *);
bcopy((caddr_t)p->ipopt_list, (caddr_t)(ip + 1), (unsigned)optlen);
*phlen = sizeof(struct ip) + optlen;
ip->ip_len += optlen;
return (m);
}
/*
* Copy options from ip to jp,
* omitting those not copied during fragmentation.
*/
int
ip_optcopy(ip, jp)
struct ip *ip, *jp;
{
register u_char *cp, *dp;
int opt, optlen, cnt;
cp = (u_char *)(ip + 1);
dp = (u_char *)(jp + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP) {
/* Preserve for IP mcast tunnel's LSRR alignment. */
*dp++ = IPOPT_NOP;
optlen = 1;
continue;
} else
optlen = cp[IPOPT_OLEN];
/* bogus lengths should have been caught by ip_dooptions */
if (optlen > cnt)
optlen = cnt;
if (IPOPT_COPIED(opt)) {
bcopy((caddr_t)cp, (caddr_t)dp, (unsigned)optlen);
dp += optlen;
}
}
for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++)
*dp++ = IPOPT_EOL;
return (optlen);
}
/*
* IP socket option processing.
*/
int
ip_ctloutput(op, so, level, optname, mp)
int op;
struct socket *so;
int level, optname;
struct mbuf **mp;
{
register struct inpcb *inp = sotoinpcb(so);
register struct mbuf *m = *mp;
register int optval = 0;
#ifdef IPSEC
struct proc *p = curproc; /* XXX */
struct tdb *tdb;
struct tdb_ident *tdbip, tdbi;
int s;
#endif
int error = 0;
if (level != IPPROTO_IP) {
error = EINVAL;
if (op == PRCO_SETOPT && *mp)
(void) m_free(*mp);
} else switch (op) {
case PRCO_SETOPT:
switch (optname) {
case IP_OPTIONS:
#ifdef notyet
case IP_RETOPTS:
return (ip_pcbopts(optname, &inp->inp_options, m));
#else
return (ip_pcbopts(&inp->inp_options, m));
#endif
case IP_TOS:
case IP_TTL:
case IP_RECVOPTS:
case IP_RECVRETOPTS:
case IP_RECVDSTADDR:
if (m == NULL || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optname) {
case IP_TOS:
inp->inp_ip.ip_tos = optval;
break;
case IP_TTL:
inp->inp_ip.ip_ttl = optval;
break;
#define OPTSET(bit) \
if (optval) \
inp->inp_flags |= bit; \
else \
inp->inp_flags &= ~bit;
case IP_RECVOPTS:
OPTSET(INP_RECVOPTS);
break;
case IP_RECVRETOPTS:
OPTSET(INP_RECVRETOPTS);
break;
case IP_RECVDSTADDR:
OPTSET(INP_RECVDSTADDR);
break;
}
}
break;
#undef OPTSET
case IP_MULTICAST_IF:
case IP_MULTICAST_TTL:
case IP_MULTICAST_LOOP:
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
error = ip_setmoptions(optname, &inp->inp_moptions, m);
break;
case IP_PORTRANGE:
if (m == 0 || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optval) {
case IP_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags &= ~(INP_HIGHPORT);
break;
case IP_PORTRANGE_HIGH:
inp->inp_flags &= ~(INP_LOWPORT);
inp->inp_flags |= INP_HIGHPORT;
break;
case IP_PORTRANGE_LOW:
inp->inp_flags &= ~(INP_HIGHPORT);
inp->inp_flags |= INP_LOWPORT;
break;
default:
error = EINVAL;
break;
}
}
break;
case IPSEC_OUTSA:
#ifndef IPSEC
error = EINVAL;
#else
s = spltdb();
if (m == 0 || m->m_len != sizeof(struct tdb_ident)) {
error = EINVAL;
} else {
tdbip = mtod(m, struct tdb_ident *);
tdb = gettdb(tdbip->spi, &tdbip->dst,
tdbip->proto);
if (tdb == NULL)
error = ESRCH;
else
tdb_add_inp(tdb, inp);
}
splx(s);
#endif /* IPSEC */
break;
case IP_AUTH_LEVEL:
case IP_ESP_TRANS_LEVEL:
case IP_ESP_NETWORK_LEVEL:
#ifndef IPSEC
error = EINVAL;
#else
if (m == 0 || m->m_len != sizeof(int)) {
error = EINVAL;
break;
}
optval = *mtod(m, u_char *);
if (optval < IPSEC_LEVEL_BYPASS ||
optval > IPSEC_LEVEL_UNIQUE) {
error = EINVAL;
break;
}
switch (optname) {
case IP_AUTH_LEVEL:
if (optval < ipsec_auth_default_level &&
suser(p->p_ucred, &p->p_acflag)) {
error = EACCES;
break;
}
inp->inp_seclevel[SL_AUTH] = optval;
break;
case IP_ESP_TRANS_LEVEL:
if (optval < ipsec_esp_trans_default_level &&
suser(p->p_ucred, &p->p_acflag)) {
error = EACCES;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -