📄 ip_output.c
字号:
}
if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr))
goto bad;
*pcbopt = m;
return (0);
bad:
(void)m_free(m);
return (EINVAL);
}
/*
* XXX
* The whole multicast option thing needs to be re-thought.
* Several of these options are equally applicable to non-multicast
* transmission, and one (IP_MULTICAST_TTL) totally duplicates a
* standard option (IP_TTL).
*/
/*
* following RFC1724 section 3.3, 0.0.0.0/8 is interpreted as interface index.
*/
static struct ifnet *
ip_multicast_if(a, ifindexp)
struct in_addr *a;
int *ifindexp;
{
int ifindex;
struct ifnet *ifp;
if (ifindexp)
*ifindexp = 0;
if (ntohl(a->s_addr) >> 24 == 0) {
ifindex = ntohl(a->s_addr) & 0xffffff;
if (ifindex < 0 || if_index < ifindex)
return NULL;
ifp = ifindex2ifnet[ifindex];
if (ifindexp)
*ifindexp = ifindex;
} else {
INADDR_TO_IFP(*a, ifp);
}
return ifp;
}
/*
* Set the IP multicast options in response to user setsockopt().
*/
static int
ip_setmoptions(sopt, imop)
struct sockopt *sopt;
struct ip_moptions **imop;
{
int error = 0;
int i;
struct in_addr addr;
struct ip_mreq mreq;
struct ifnet *ifp;
struct ip_moptions *imo = *imop;
struct route ro;
struct sockaddr_in *dst;
int ifindex;
int s;
if (imo == NULL) {
/*
* No multicast option buffer attached to the pcb;
* allocate one and initialize to default values.
*/
imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS,
M_WAITOK);
if (imo == NULL)
return (ENOBUFS);
*imop = imo;
imo->imo_multicast_ifp = NULL;
imo->imo_multicast_addr.s_addr = INADDR_ANY;
imo->imo_multicast_vif = -1;
imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
imo->imo_num_memberships = 0;
}
switch (sopt->sopt_name) {
/* store an index number for the vif you wanna use in the send */
case IP_MULTICAST_VIF:
if (legal_vif_num == 0) {
error = EOPNOTSUPP;
break;
}
error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
if (error)
break;
if (!legal_vif_num(i) && (i != -1)) {
error = EINVAL;
break;
}
imo->imo_multicast_vif = i;
break;
case IP_MULTICAST_IF:
/*
* Select the interface for outgoing multicast packets.
*/
error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr);
if (error)
break;
/*
* INADDR_ANY is used to remove a previous selection.
* When no interface is selected, a default one is
* chosen every time a multicast packet is sent.
*/
if (addr.s_addr == INADDR_ANY) {
imo->imo_multicast_ifp = NULL;
break;
}
/*
* The selected interface is identified by its local
* IP address. Find the interface and confirm that
* it supports multicasting.
*/
s = splimp();
ifp = ip_multicast_if(&addr, &ifindex);
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
splx(s);
error = EADDRNOTAVAIL;
break;
}
imo->imo_multicast_ifp = ifp;
if (ifindex)
imo->imo_multicast_addr = addr;
else
imo->imo_multicast_addr.s_addr = INADDR_ANY;
splx(s);
break;
case IP_MULTICAST_TTL:
/*
* Set the IP time-to-live for outgoing multicast packets.
* The original multicast API required a char argument,
* which is inconsistent with the rest of the socket API.
* We allow either a char or an int.
*/
if (sopt->sopt_valsize == 1) {
u_char ttl;
error = sooptcopyin(sopt, &ttl, 1, 1);
if (error)
break;
imo->imo_multicast_ttl = ttl;
} else {
u_int ttl;
error = sooptcopyin(sopt, &ttl, sizeof ttl,
sizeof ttl);
if (error)
break;
if (ttl > 255)
error = EINVAL;
else
imo->imo_multicast_ttl = ttl;
}
break;
case IP_MULTICAST_LOOP:
/*
* Set the loopback flag for outgoing multicast packets.
* Must be zero or one. The original multicast API required a
* char argument, which is inconsistent with the rest
* of the socket API. We allow either a char or an int.
*/
if (sopt->sopt_valsize == 1) {
u_char loop;
error = sooptcopyin(sopt, &loop, 1, 1);
if (error)
break;
imo->imo_multicast_loop = !!loop;
} else {
u_int loop;
error = sooptcopyin(sopt, &loop, sizeof loop,
sizeof loop);
if (error)
break;
imo->imo_multicast_loop = !!loop;
}
break;
case IP_ADD_MEMBERSHIP:
/*
* Add a multicast group membership.
* Group must be a valid IP multicast address.
*/
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
if (error)
break;
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
error = EINVAL;
break;
}
s = splimp();
/*
* If no interface address was provided, use the interface of
* the route to the given multicast address.
*/
if (mreq.imr_interface.s_addr == INADDR_ANY) {
bzero((caddr_t)&ro, sizeof(ro));
dst = (struct sockaddr_in *)&ro.ro_dst;
dst->sin_len = sizeof(*dst);
dst->sin_family = AF_INET;
dst->sin_addr = mreq.imr_multiaddr;
rtalloc(&ro);
if (ro.ro_rt == NULL) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
ifp = ro.ro_rt->rt_ifp;
rtfree(ro.ro_rt);
}
else {
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
}
/*
* See if we found an interface, and confirm that it
* supports multicast.
*/
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
/*
* See if the membership already exists or if all the
* membership slots are full.
*/
for (i = 0; i < imo->imo_num_memberships; ++i) {
if (imo->imo_membership[i]->inm_ifp == ifp &&
imo->imo_membership[i]->inm_addr.s_addr
== mreq.imr_multiaddr.s_addr)
break;
}
if (i < imo->imo_num_memberships) {
error = EADDRINUSE;
splx(s);
break;
}
if (i == IP_MAX_MEMBERSHIPS) {
error = ETOOMANYREFS;
splx(s);
break;
}
/*
* Everything looks good; add a new record to the multicast
* address list for the given interface.
*/
if ((imo->imo_membership[i] =
in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) {
error = ENOBUFS;
splx(s);
break;
}
++imo->imo_num_memberships;
splx(s);
break;
case IP_DROP_MEMBERSHIP:
/*
* Drop a multicast group membership.
* Group must be a valid IP multicast address.
*/
error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq);
if (error)
break;
if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) {
error = EINVAL;
break;
}
s = splimp();
/*
* If an interface address was specified, get a pointer
* to its ifnet structure.
*/
if (mreq.imr_interface.s_addr == INADDR_ANY)
ifp = NULL;
else {
ifp = ip_multicast_if(&mreq.imr_interface, NULL);
if (ifp == NULL) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
}
/*
* Find the membership in the membership array.
*/
for (i = 0; i < imo->imo_num_memberships; ++i) {
if ((ifp == NULL ||
imo->imo_membership[i]->inm_ifp == ifp) &&
imo->imo_membership[i]->inm_addr.s_addr ==
mreq.imr_multiaddr.s_addr)
break;
}
if (i == imo->imo_num_memberships) {
error = EADDRNOTAVAIL;
splx(s);
break;
}
/*
* Give up the multicast address record to which the
* membership points.
*/
in_delmulti(imo->imo_membership[i]);
/*
* Remove the gap in the membership array.
*/
for (++i; i < imo->imo_num_memberships; ++i)
imo->imo_membership[i-1] = imo->imo_membership[i];
--imo->imo_num_memberships;
splx(s);
break;
default:
error = EOPNOTSUPP;
break;
}
/*
* If all options have default values, no need to keep the mbuf.
*/
if (imo->imo_multicast_ifp == NULL &&
imo->imo_multicast_vif == -1 &&
imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL &&
imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP &&
imo->imo_num_memberships == 0) {
free(*imop, M_IPMOPTS);
*imop = NULL;
}
return (error);
}
/*
* Return the IP multicast options in response to user getsockopt().
*/
static int
ip_getmoptions(sopt, imo)
struct sockopt *sopt;
register struct ip_moptions *imo;
{
struct in_addr addr;
struct in_ifaddr *ia;
int error, optval;
u_char coptval;
error = 0;
switch (sopt->sopt_name) {
case IP_MULTICAST_VIF:
if (imo != NULL)
optval = imo->imo_multicast_vif;
else
optval = -1;
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
case IP_MULTICAST_IF:
if (imo == NULL || imo->imo_multicast_ifp == NULL)
addr.s_addr = INADDR_ANY;
else if (imo->imo_multicast_addr.s_addr) {
/* return the value user has set */
addr = imo->imo_multicast_addr;
} else {
IFP_TO_IA(imo->imo_multicast_ifp, ia);
addr.s_addr = (ia == NULL) ? INADDR_ANY
: IA_SIN(ia)->sin_addr.s_addr;
}
error = sooptcopyout(sopt, &addr, sizeof addr);
break;
case IP_MULTICAST_TTL:
if (imo == 0)
optval = coptval = IP_DEFAULT_MULTICAST_TTL;
else
optval = coptval = imo->imo_multicast_ttl;
if (sopt->sopt_valsize == 1)
error = sooptcopyout(sopt, &coptval, 1);
else
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
case IP_MULTICAST_LOOP:
if (imo == 0)
optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
else
optval = coptval = imo->imo_multicast_loop;
if (sopt->sopt_valsize == 1)
error = sooptcopyout(sopt, &coptval, 1);
else
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
default:
error = ENOPROTOOPT;
break;
}
return (error);
}
/*
* Discard the IP multicast options.
*/
void
ip_freemoptions(imo)
register struct ip_moptions *imo;
{
register int i;
if (imo != NULL) {
for (i = 0; i < imo->imo_num_memberships; ++i)
in_delmulti(imo->imo_membership[i]);
free(imo, M_IPMOPTS);
}
}
/*
* Routine called from ip_output() to loop back a copy of an IP multicast
* packet to the input queue of a specified interface. Note that this
* calls the output routine of the loopback "driver", but with an interface
* pointer that might NOT be a loopback interface -- evil, but easier than
* replicating that code here.
*/
static void
ip_mloopback(ifp, m, dst, hlen)
struct ifnet *ifp;
register struct mbuf *m;
register struct sockaddr_in *dst;
int hlen;
{
register struct ip *ip;
struct mbuf *copym;
copym = m_copy(m, 0, M_COPYALL);
if (copym != NULL && (copym->m_flags & M_EXT || copym->m_len < hlen))
copym = m_pullup(copym, hlen);
if (copym != NULL) {
/*
* We don't bother to fragment if the IP length is greater
* than the interface's MTU. Can this possibly matter?
*/
ip = mtod(copym, struct ip *);
HTONS(ip->ip_len);
HTONS(ip->ip_off);
ip->ip_sum = 0;
if (ip->ip_vhl == IP_VHL_BORING) {
ip->ip_sum = in_cksum_hdr(ip);
} else {
ip->ip_sum = in_cksum(copym, hlen);
}
/*
* NB:
* It's not clear whether there are any lingering
* reentrancy problems in other areas which might
* be exposed by using ip_input directly (in
* particular, everything which modifies the packet
* in-place). Yet another option is using the
* protosw directly to deliver the looped back
* packet. For the moment, we'll err on the side
* of safety by using if_simloop().
*/
#if 1 /* XXX */
if (dst->sin_family != AF_INET) {
printf("ip_mloopback: bad address family %d\n",
dst->sin_family);
dst->sin_family = AF_INET;
}
#endif
#ifdef notdef
copym->m_pkthdr.rcvif = ifp;
ip_input(copym);
#else
/* if the checksum hasn't been computed, mark it as valid */
if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
copym->m_pkthdr.csum_flags |=
CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
copym->m_pkthdr.csum_data = 0xffff;
}
if_simloop(ifp, copym, dst->sin_family, 0);
#endif
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -