- 论坛徽章:
- 0
|
/*
* ethernet网络层代码详解
* 解释 ie_minix
*/
#include "opt_atalk.h"
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipx.h"
#include "opt_bdg.h"
#include "opt_netgraph.h"
#include <sys/param.h>;
#include <sys/systm.h>;
#include <sys/kernel.h>;
#include <sys/malloc.h>;
#include <sys/mbuf.h>;
#include <sys/socket.h>;
#include <sys/sockio.h>;
#include <sys/sysctl.h>;
#include <net/if.h>;
#include <net/netisr.h>;
#include <net/route.h>;
#include <net/if_llc.h>;
#include <net/if_dl.h>;
#include <net/if_types.h>;
#include <net/bpf.h>;
#include <net/ethernet.h>;
#if defined(INET) || defined(INET6)
#include <netinet/in.h>;
#include <netinet/in_var.h>;
#include <netinet/if_ether.h>;
#endif
/*vlan的代码我也去掉了*/
#include "vlan.h"
#if NVLAN >; 0
#include <net/if_vlan_var.h>;
#endif /* NVLAN >; 0 */
/* netgraph 相关函数用于PPPoE协议即ADSL,对不起大家,我去掉了这些函数的说明,如果那位感兴趣,可以加上 */
void (*ng_ether_input_p)(struct ifnet *ifp,
struct mbuf **mp, struct ether_header *eh);
void (*ng_ether_input_orphan_p)(struct ifnet *ifp,
struct mbuf *m, struct ether_header *eh);
int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
void (*ng_ether_attach_p)(struct ifnet *ifp);
void (*ng_ether_detach_p)(struct ifnet *ifp);
static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **,
struct sockaddr *));
u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
#define senderr(e) do { error = (e); goto bad;} while (0)
#define IFP2AC(IFP) ((struct arpcom *)IFP)
/*
* 以太网输出子程序.在此程序中,我省去了很多对我们没有很大用处的东西,如IPX协议,APPLETALK协议,IPV6等,还有
* 一些BPF过滤,放火墙等代码.
*/
int
ether_output(ifp, m, dst, rt0)
register struct ifnet *ifp;
struct mbuf *m;
struct sockaddr *dst;
/*
* 该结构来自于sys/socket.h 核心用于存储大多数地址.
*
struct sockaddr {
u_char sa_len; /* 总长度
sa_family_t sa_family; /* 地址族
char sa_data[14]; /* 地址值
};
*/
struct rtentry *rt0;
/*路由结构说明
struct rtentry {
struct radix_node rt_nodes[2]; /* radix树 /
#define rt_key(r) ((struct sockaddr *)((r)->;rt_nodes->;rn_key))
#define rt_mask(r) ((struct sockaddr *)((r)->;rt_nodes->;rn_mask))
struct sockaddr *rt_gateway; /* 网关 *
long rt_refcnt; /* # 保留参考 *
u_long rt_flags; /* 已经启动up/或接口已经关闭down?, 是主机路由还是网络路由 *
struct ifnet *rt_ifp; /* 使用的接口 /
struct ifaddr *rt_ifa; /* 接口在使用 *
struct sockaddr *rt_genmask; /* 克窿路由产生的 *
caddr_t rt_llinfo; /* 指向链路层信息 *
struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols *
struct rtentry *rt_gwroute; /* 网关路由入口 /
int (*rt_output) __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *));
/* 输出程序 *
struct rtentry *rt_parent; /* 该路由的克隆父路由 /
void *rt_filler2;
};
*/
{
short type;
int error = 0, hdrcmplt = 0;
u_char esrc[6], edst[6];
register struct rtentry *rt;
register struct ether_header *eh;
int off, loop_copy = 0;
int hlen; /* 链路层头长度 */
struct arpcom *ac = IFP2AC(ifp);
/*接口打开了吗*/
if ((ifp->;if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))/*接口启动了吗?*/
senderr(ENETDOWN);
rt = rt0;/*rt也指向路由表*/
if (rt) {/*如果路由指针为真*/
if ((rt->;rt_flags & RTF_UP) == 0) {/*如果路由关闭重新取路由*/
rt0 = rt = rtalloc1(dst, 1, 0UL);
if (rt0)/*路由找到了*/
rt->;rt_refcnt--;
else
senderr(EHOSTUNREACH);/*否则发送主机没找到*/
}
if (rt->;rt_flags & RTF_GATEWAY) {/*标识中有网关*/
if (rt->;rt_gwroute == 0)/*该项不是网关就再找*/
goto lookup;
if (((rt = rt->;rt_gwroute)->;rt_flags & RTF_UP) == 0) {/*网关没启动*/
rtfree(rt); rt = rt0;
lookup: rt->;rt_gwroute = rtalloc1(rt->;rt_gateway, 1,
0UL);
if ((rt = rt->;rt_gwroute) == 0)
senderr(EHOSTUNREACH);
}
}
if (rt->;rt_flags & RTF_REJECT)
if (rt->;rt_rmx.rmx_expire == 0 ||
time_second < rt->;rt_rmx.rmx_expire)
senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
hlen = ETHER_HDR_LEN;/*=14*/
switch (dst->;sa_family) {/*查看地址族*/
#ifdef INET
case AF_INET:/*是IP协议*/
if (!arpresolve(ac, rt, m, dst, edst, rt0))/*调用ARP的地址解释例程*/
return (0); /* 还是解释不了 */
off = m->;m_pkthdr.len - m->;m_len;/*总长度=包的长度-目前mbuf的长度*/
type = htons(ETHERTYPE_IP);/*主机到网络方式*/
break;
#endif
case pseudo_AF_HDRCMPLT:
hdrcmplt = 1;
eh = (struct ether_header *)dst->;sa_data;
(void)memcpy(esrc, eh->;ether_shost, sizeof (esrc));
/*上面这一段比较有意思,是以太网的头部的源地址和目的地址对调,一般用于ARP的代理*/
case AF_UNSPEC:/*一般由ARP的ARP请求发送例程调用*/
loop_copy = -1;
eh = (struct ether_header *)dst->;sa_data;/*构造发送的以太网头部*/
(void)memcpy(edst, eh->;ether_dhost, sizeof (edst));
type = eh->;ether_type;
break;
default:/*如果到了这,那肯定出错了*/
printf("%s%d: can't handle af%d\n", ifp->;if_name, ifp->;if_unit,
dst->;sa_family);
senderr(EAFNOSUPPORT);
}
/*
* 加上局域网头部. 如果在第一个mbuf中没有空间,那么再分配另外一个mbuf
*/
M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);/*分配一mbuf,前面保留以太网头部的长度空间*/
if (m == 0)
senderr(ENOBUFS);
eh = mtod(m, struct ether_header *);/*定位以太网头*/
(void)memcpy(&eh->;ether_type, &type,
sizeof(eh->;ether_type));
(void)memcpy(eh->;ether_dhost, edst, sizeof (edst));/*拷贝目的地址*/
if (hdrcmplt)
(void)memcpy(eh->;ether_shost, esrc,
sizeof(eh->;ether_shost));
else
(void)memcpy(eh->;ether_shost, ac->;ac_enaddr,/*拷贝源地址,在xie_ipfw中要从ether_input中的源地址拷贝过来*/
sizeof(eh->;ether_shost));
/*
* 如果一个单工接口, 并且有一包到达地址是本卡的地址或广播地址,或环回.
* XXX 为了使一个单工设备做起来象一双工设备
*/
if ((ifp->;if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
if ((m->;m_flags & M_BCAST) || (loop_copy >; 0)) {
struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
(void) if_simloop(ifp, n, dst->;sa_family, hlen);
} else if (bcmp(eh->;ether_dhost,
eh->;ether_shost, ETHER_ADDR_LEN) == 0) {
(void) if_simloop(ifp, m, dst->;sa_family, hlen);
return (0); /* XXX */
}
}
/* PPPoe协议,我们可不管他 */
if (ng_ether_output_p != NULL) {
if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) {
bad: if (m != NULL)
m_freem(m);
return (error);
}
if (m == NULL)
return (0);
}
/* 以太网头准备完毕,调用以太网祯输出*/
return ether_output_frame(ifp, m);
}
/*以太网链路层输出例程发送一帧数据
*/
int
ether_output_frame(ifp, m)
struct ifnet *ifp;
struct mbuf *m;
{
int s, error = 0;
s = splimp();/*在对队列和输出开始操作前屏蔽网络中断*/
if (IF_QFULL(&ifp->;if_snd)) {/*发送队列满了吗?*/
IF_DROP(&ifp->;if_snd);
splx(s);
m_freem(m);
return (ENOBUFS);
}
ifp->;if_obytes += m->;m_pkthdr.len;
if (m->;m_flags & M_MCAST)
ifp->;if_omcasts++;
IF_ENQUEUE(&ifp->;if_snd, m);/*把mbuf编入队列*/
if ((ifp->;if_flags & IFF_OACTIVE) == 0)/*接口正在输出吗?*/
(*ifp->;if_start)(ifp);
splx(s);
return (error);
}
/* 处理一收到的以太网包,此包在一个无以太网头部的mbuf链中
* (其实在此过程中并没处理封包,而是传导到ether_demux中处理,在这可以安排自己的代码)
* 调用是由网卡驱动程序调用,可参考我的网卡驱动程序详解,在这我去掉了一些不相关的代码
* 如:IPX, APPALTALK,IPV6,IPFW,DUMMYNET等
*/
void
ether_input(ifp, eh, m)
struct ifnet *ifp; /*接收到以太网封包的网络适配器的ifnet*/
struct ether_header *eh; /*以太网头部,成员为*/
/* 10M以太网络头部结构.
struct ether_header {
u_char ether_dhost[ETHER_ADDR_LEN]; 目的主机地址
u_char ether_shost[ETHER_ADDR_LEN]; 源主机地址
u_short ether_type; 以太网络类型
}; */
struct mbuf *m;
{
/*---------------------------------------从这到下面都是过滤用的---------------------------------------------*/
/* PPPoE支持(ADSL) */
if (ng_ether_input_p != NULL) {
(*ng_ether_input_p)(ifp, &m, eh);
if (m == NULL)
return;
}
/* -------------------------------------------到此为止,这意味着可以加入自己的代码----------------------------------- */
ether_demux(ifp, eh, m);
}
/*
* 真正处理以太网封包.
*/
void
ether_demux(ifp, eh, m) /*该三参数和ether_input()函数的参数一样*/
struct ifnet *ifp;
struct ether_header *eh;
struct mbuf *m;
{
struct ifqueue *inq; /*
* 结构定义了一个网络接口队列.
struct ifqueue {
struct mbuf *ifq_head; /*封包的队列头指针
struct mbuf *ifq_tail; /*尾指针
int ifq_len; /*队列长度
int ifq_maxlen; /*队列的最大长度
int ifq_drops; /*丢弃的数量
};
*/
u_short ether_type;
int s;
if ((ifp->;if_flags & IFF_PROMISC) != 0 /*不在混杂模式,目的主机硬件地址的第一字节的最后一位是0即非多播地址*/
&& (eh->;ether_dhost[0] & 1) == 0 /*不是本机地址.注意:在XIE_FIREWALL中不能使用之,应调用一函数*/
&& bcmp(eh->;ether_dhost, /*该函数功能是先将其转化为IP头部,取目的IP地址(是本地网吗?是),再查*/
IFP2AC(ifp)->;ac_enaddr, ETHER_ADDR_LEN) != 0) {/*对应的硬件地址,该硬件地址如为空则发出ARP请求*/
m_freem(m); /*本地网络如下图:*/
return;
}
/* 以下是我的一些想发,太可笑了,现在看来
int duan=3 (即分3个网段)
SYSCTL_NODE(_net, PF_LINK, xie, CTLFLAG_RW, 0, "Link layers" ;
SYSCTL_NODE(_net_xie,OID_AUTO, link, CTLFLAG_RW, 0, "xie link-management" ;
SYSCTL_INT(_net_xie_link, OID_AUTO, fr_flags, CTLFLAG_RW, &duan, 0, "" ;
SYSCTL_INT(_net_xie_link, OID_AUTO, fr_pass, CTLFLAG_RW, &xie_gateway, 0, "" ;
SYSCTL_INT(_net_xie_link, OID_AUTO, fr_active, CTLFLAG_RD, &xie_gatewaymask, 0, "" ;
SYSCTL_INT(....);用sysctl可更改为2,4等,看你支持几块卡
int xie_gateway=... 即210.35.7.1的32位数
SYSCTL_INT(....);网关地址控制
int xie_gatewaymask=... 即255.255.255.0
SYSCTL_INT(....):网关掩码
struct xie_ipfw { 内网受保护的IP地址及网段
struct ifnet *ifp ; 所在网卡的ifnet
int duan_xie;所在段
int area_xie;段的范围标识,0为相等,1为小于,2为在什么之间
struct in_addr area_dy; 等于的IP值,
struct in_addr area_max;最大的IP
struct in_addr area_min;最小的IP
int inok_duan; 允许进入的段,
int outok_duan;允许出去的段
struct in_addr net_address;该段的网络地址,即210.35.7.0
};
该结构在启动后进行设置,第一层过滤在ok_duan的规则中,在主机中有几块网卡就有几个该结构,每一个网卡所在的段号不一样
段的范围标识表示该段中是只有一个IP地址还是中间的一段IP地址或小于那一段IP地址,在其下的结构表示要用到的IP地址.
在inok_duan中主要是一些常量,0为0段可进入,1为1段可进入,一直到duan,且段的最大值为24,25为所有段,26为同一网络地址
outok_duan同inok_duan一样,不过是出口.net_address代表是该段的网络地址,他代表在一个段中必须不能有不同的网络地址
所以我目前只支持C类地址.
if (INCLASSC(xie_gateway)) B类地址和A类地址不处理
struct xie_ipaddr { 整个网段的IP地址列表,如210.35.7.0网段
struct in_addr ip_xie; 对方的IP地址
struct xie_ipfw *my_duan;所在段的回指针
struct ifnet *my_ifnet;
struct ether_header myhd; 对方的硬件地址
}
struct xie_port { 保护的端口
int xie_protocol; 受保护的协议 xie_protocol =0 为TCP,=1 为UDP
byte xie_port_in; 受保护的端口号范围(ether_input) xie_prot_in =0 为 ALL,1 为单个端口,2 为大于,3 为小于,4 为大于且小于.
int xie_big_in; 大于使用的最大值,
int xie_sma_in; 小于使用,等于使用.
byte xie_port_out; ether_output.
int xie_big_out;
int xie_sma_out;
}
在 int ifioctl(so, cmd, data, p) 中加入(此函数在if.c中)
switch (cmd) {
case SIOCSIFXIEFIREWALL:
for(i=1,i=duan,i++)
为每段分配IP地址;
---------------------------------------------------------------------------------------------------------------
| 网卡 1 (防火墙内主机1) | 网卡 2 (防火墙内主机2) | 网卡 2 (防火墙内主机3,4,5,6) | 网卡 3 (非军事化区) |
---------------------------------------------------------------------------------------------------------------
| 210.35.7.2 (ORACLE-8i) | 210.35.7.3 (NETSERVER) | 210.35.7.10 -50 (现场端) | 210.35.7.100-200(院校端)|
---------------------------------------------------------------------------------------------------------------
*/
/* 如果接口没启动则放弃包 */
if ((ifp->;if_flags & IFF_UP) == 0) {
m_freem(m);
return;
}
ifp->;if_ibytes += m->;m_pkthdr.len + sizeof (*eh); /*接口的接收到的字节数,eh为以太网包头*/
if (eh->;ether_dhost[0] & 1) {/*以太网的包头的目的地址(硬件)的第一字节不为全0则为广播或多播包*/
if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->;ether_dhost,/*是广播还是多播*/
sizeof(etherbroadcastaddr)) == 0)
m->;m_flags |= M_BCAST;
else
m->;m_flags |= M_MCAST;
}
if (m->;m_flags & (M_BCAST|M_MCAST))
ifp->;if_imcasts++;
ether_type = ntohs(eh->;ether_type);/*网络到主机方式*/
switch (ether_type) {
#ifdef INET
case ETHERTYPE_IP:/*是IP协议吗?*/
if (ipflow_fastforward(m))
return;
schednetisr(NETISR_IP);/*调用软中断*/
inq = &/*IP对列*/
break;
case ETHERTYPE_ARP:/*是ARP协议吗?*/
if (ifp->;if_flags & IFF_NOARP) {
/* 如果接口不支持ARP方式,释放包 */
m_freem(m);
return;
}
schednetisr(NETISR_ARP);
inq = &
break;
#endif
default:
}
s = splimp();/*关网络中断*/
if (IF_QFULL(inq)) {
/*#原型是define IF_QFULL(ifq) ((ifq)->;ifq_len >;= (ifq)->;ifq_maxlen) 队列满*/
IF_DROP(inq);
/*原型是#define IF_DROP(ifq) ((ifq)->;ifq_drops++) 丢弃数加1*/
m_freem(m);
} else
IF_ENQUEUE(inq, m);
/*以下是原型
#define IF_ENQUEUE(ifq, m) { \
(m)->;m_nextpkt = 0; \
if ((ifq)->;ifq_tail == 0) \ 如果队列尾巴为没有,则该队列没初始化
(ifq)->;ifq_head = m; \ 初始化队列头为M
else \ 有尾巴,即该队列已经有mbuf
(ifq)->;ifq_tail->;m_nextpkt = m; \ 当前的尾巴mbuf的下一mbuf指向m
(ifq)->;ifq_tail = m; \ 队列的尾巴指向m
(ifq)->;ifq_len++; \ 队列长度加1
}
*/
splx(s); /*开网络中断*/
}
/*
* 当接口链入到接口列表时执行的普通动作
*/
void/*在我的网络设备驱动程序中有描写, 主要是由网卡驱动调用*/
ether_ifattach(ifp, bpf)
register struct ifnet *ifp;
int bpf;
{
register struct ifaddr *ifa;
register struct sockaddr_dl *sdl;
if_attach(ifp);/*调用if.c中的过程*/
ifp->;if_type = IFT_ETHER;
ifp->;if_addrlen = 6;
ifp->;if_hdrlen = 14;
ifp->;if_mtu = ETHERMTU;/*1500*/
ifp->;if_resolvemulti = ether_resolvemulti;/*解释多播的过程指针*/
if (ifp->;if_baudrate == 0) /*初始化波特率*/
ifp->;if_baudrate = 10000000;
ifa = ifnet_addrs[ifp->;if_index - 1];
KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__));
sdl = (struct sockaddr_dl *)ifa->;ifa_addr;
sdl->;sdl_type = IFT_ETHER;
sdl->;sdl_alen = ifp->;if_addrlen;
bcopy((IFP2AC(ifp))->;ac_enaddr, LLADDR(sdl), ifp->;if_addrlen);
if (bpf)/*BPF支持*/
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
if (ng_ether_attach_p != NULL)/*PPPoE支持即ADSL*/
(*ng_ether_attach_p)(ifp);
}
void
ether_ifdetach(ifp, bpf)
struct ifnet *ifp;
int bpf;
{
if (ng_ether_detach_p != NULL)
(*ng_ether_detach_p)(ifp);
if (bpf)
bpfdetach(ifp);
if_detach(ifp);
}
SYSCTL_DECL(_net_link);/*申明从哪个根接上*/
SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet" ;/*做一个节点*/
int /*inctl控制代码和多播代码我就不写了,太平常了*/
ether_ioctl(ifp, command, data)
struct ifnet *ifp;
int command;
caddr_t data;
{
struct ifaddr *ifa = (struct ifaddr *) data;
struct ifreq *ifr = (struct ifreq *) data;
int error = 0;
switch (command) {
case SIOCSIFADDR:
ifp->;if_flags |= IFF_UP;
switch (ifa->;ifa_addr->;sa_family) {
#ifdef INET
case AF_INET:
ifp->;if_init(ifp->;if_softc); /* before arpwhohas */
arp_ifinit(IFP2AC(ifp), ifa);
break;
#endif
default:
ifp->;if_init(ifp->;if_softc);
break;
}
break;
case SIOCGIFADDR:
{
struct sockaddr *sa;
sa = (struct sockaddr *) & ifr->;ifr_data;
bcopy(IFP2AC(ifp)->;ac_enaddr,
(caddr_t) sa->;sa_data, ETHER_ADDR_LEN);
}
break;
case SIOCSIFMTU:
/*
* Set the interface MTU.
*/
if (ifr->;ifr_mtu >; ETHERMTU) {
error = EINVAL;
} else {
ifp->;if_mtu = ifr->;ifr_mtu;
}
break;
}
return (error);
}
int
ether_resolvemulti(ifp, llsa, sa)
struct ifnet *ifp;
struct sockaddr **llsa;
struct sockaddr *sa;
{
struct sockaddr_dl *sdl;
struct sockaddr_in *sin;
u_char *e_addr;
switch(sa->;sa_family) {
case AF_LINK:
/*
* No mapping needed. Just check that it's a valid MC address.
*/
sdl = (struct sockaddr_dl *)sa;
e_addr = LLADDR(sdl);
if ((e_addr[0] & 1) != 1)
return EADDRNOTAVAIL;
*llsa = 0;
return 0;
#ifdef INET
case AF_INET:
sin = (struct sockaddr_in *)sa;
if (!IN_MULTICAST(ntohl(sin->;sin_addr.s_addr)))
return EADDRNOTAVAIL;
MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
M_WAITOK|M_ZERO);
sdl->;sdl_len = sizeof *sdl;
sdl->;sdl_family = AF_LINK;
sdl->;sdl_index = ifp->;if_index;
sdl->;sdl_type = IFT_ETHER;
sdl->;sdl_alen = ETHER_ADDR_LEN;
e_addr = LLADDR(sdl);
ETHER_MAP_IP_MULTICAST(&sin->;sin_addr, e_addr);
*llsa = (struct sockaddr *)sdl;
return 0;
#endif
default:
/*
* Well, the text isn't quite right, but it's the name
* that counts...
*/
return EAFNOSUPPORT;
}
} |
|