ip_output.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,972 行 · 第 1/4 页

C
1,972
字号
	}	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 intip_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 intip_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. */voidip_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 voidip_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 + =
减小字号Ctrl + -
显示快捷键?