linux网口数据传输,linux协议栈之链路层上的数据传输(向网桥添加接口(四))

前面已经分析了,将接口添进网桥时,用户空间调用ioctl(br_socket_fd, SIOCBRADDIF, &ifr)

注意到在void br_dev_setup(struct net_device *dev)中已经对dev->do_ioctl进行了赋值,即:

dev->do_ioctl = br_dev_ioctl

进行ioctl进行访问的时候,进入到br_dev_ioctl:(net/brige/br_ioctl.c)

int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)

{

struct net_bridge *br = netdev_priv(dev);

switch(cmd) {

case SIOCDEVPRIVATE:

return old_dev_ioctl(dev, rq, cmd);

//添加一个接口

case SIOCBRADDIF:

//删除一个接口

case SIOCBRDELIF:

return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

}

pr_debug("Bridge does not support ioctl 0x%x\n", cmd);

return -EOPNOTSUPP;

}

我们在用户空间使用的标志是SIOCBRADDIF。所以流程进入add_del_if()

static int add_del_if(struct net_bridge *br, int ifindex, int isadd)

{

struct net_device *dev;

int ret;

if (!capable(CAP_NET_ADMIN))

return -EPERM;

dev = dev_get_by_index(ifindex);

if (dev == NULL)

return -EINVAL;

if (isadd)

ret = br_add_if(br, dev);

else

ret = br_del_if(br, dev);

dev_put(dev);

return ret;

}

因为cmd == SIOCBRADDIF为真,所以调用br_add_if():

int br_add_if(struct net_bridge *br, struct net_device *dev)(net/brige/br_if.c))

{

struct net_bridge_port *p;

int err = 0;

//回环。或者非以及网接口

if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)

return -EINVAL;

//构造数据包函数为网桥类型

if (dev->hard_start_xmit == br_dev_xmit)

return -ELOOP;

//此接口已经存在于网桥

if (dev->br_port != NULL)

return -EBUSY;

//为dev创建网桥接口.dev->br_port。指向所属网桥端口

//dev->br_port->br:指向它所属的网桥

//为该接口创建net_bridge_port

if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))

return PTR_ERR(p);

//更新port->MAC对应表

if ((err = br_fdb_insert(br, p, dev->dev_addr, 1)))

destroy_nbp(p);

else if ((err = br_sysfs_addif(p)))

del_nbp(p);

else {

//设置接口为混杂模式

dev_set_promiscuity(dev, 1);

//将p->list更新至br->port_list中

list_add_rcu(&p->list, &br->port_list);

spin_lock_bh(&br->lock);

br_stp_recalculate_bridge_id(br);

if ((br->dev->flags & IFF_UP)

&& (dev->flags & IFF_UP) && netif_carrier_ok(dev))

br_stp_enable_port(p);

spin_unlock_bh(&br->lock);

dev_set_mtu(br->dev, br_min_mtu(br));

}

return err;

}

为接口创建net_bridge_port的函数为new_nbp。这个函数比较简单:

static struct net_bridge_port *new_nbp(struct net_bridge *br,

struct net_device *dev,

unsigned long cost)

{

int index;

struct net_bridge_port *p;

index = find_portno(br);

if (index < 0)

return ERR_PTR(index);

p = kmalloc(sizeof(*p), GFP_KERNEL);

if (p == NULL)

return ERR_PTR(-ENOMEM);

memset(p, 0, sizeof(*p));

p->br = br;

dev_hold(dev);

p->dev = dev;

p->path_cost = cost;

p->priority = 0x8000 >> BR_PORT_BITS;

dev->br_port = p;

p->port_no = index;

br_init_port(p);

p->state = BR_STATE_DISABLED;

kobject_init(&p->kobj);

return p;

}

之后,把要加入的接口对应的mac与接口作为本机静态项加入到prot—mac对应表。这是在br_fdb_insert()中实现的

int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,

const unsigned char *addr, int is_local)

{

int ret;

spin_lock_bh(&br->hash_lock);

ret = fdb_insert(br, source, addr, is_local);

spin_unlock_bh(&br->hash_lock);

return ret;

}

操作存在异步性,在插入之前加锁。具体的插入在fdb_insert中实现

static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,

const unsigned char *addr, int is_local)

{

struct hlist_node *h;

struct net_bridge_fdb_entry *fdb;

int hash = br_mac_hash(addr);

//判断是否为有效的mac地址

if (!is_valid_ether_addr(addr))

return -EADDRNOTAVAIL;

hlist_for_each_entry(fdb, h, &br->hash[hash], hlist) {

//如果表中已经包含了此项

if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {

//如果为本机MAC

/* attempt to update an entry for a local interface */

if (fdb->is_local) {

/* it is okay to have multiple ports with same

* address, just don't allow to be spoofed.

*/

if (is_local)

return 0;

if (net_ratelimit())

printk(KERN_WARNING "%s: received packet with "

" own address as source address\n",

source->dev->name);

return -EEXIST;

}

//如果添加的是本机IP

if (is_local) {

printk(KERN_WARNING "%s adding interface with same address "

"as a received packet\n",

source->dev->name);

goto update;

}

//如果添加的是静态MAC

//则不更新相关的信息

if (fdb->is_static)

return 0;

/* move to end of age list */

list_del(&fdb->u.age_list);

goto update;

}

}

fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);

if (!fdb)

return ENOMEM;

memcpy(fdb->addr.addr, addr, ETH_ALEN);

atomic_set(&fdb->use_count, 1);

hlist_add_head_rcu(&fdb->hlist, &br->hash[hash]);

if (!timer_pending(&br->gc_timer)) {

br->gc_timer.expires = jiffies + hold_time(br);

add_timer(&br->gc_timer);

}

update:

fdb->dst = source;

fdb->is_local = is_local;

fdb->is_static = is_local;

fdb->ageing_timer = jiffies;

if (!is_local)

list_add_tail(&fdb->u.age_list, &br->age_list);

return 0;

}

此函数先判断要插入项是否存在,若是已存在,且不为静态项,具更新对应项。若不存在该项,则分配一个net_bridge_fdb_entry,插入到CAM表

先来分析一下net_bridge_port的结构:

struct net_bridge_port

{

//当前端口所在的briage

struct net_bridge*br;

//此端口对应的物理端口

struct net_device*dev;

//同一桥内的端口链表?

struct list_headlist;

/* STP */

u8priority;

u8state;

u16port_no;

unsigned chartopology_change_ack;

unsigned charconfig_pending;

port_idport_id;

port_iddesignated_port;

bridge_iddesignated_root;

bridge_iddesignated_bridge;

u32path_cost;

u32designated_cost;

struct timer_listforward_delay_timer;

struct timer_listhold_timer;

struct timer_listmessage_age_timer;

struct kobjectkobj;

struct rcu_headrcu;

};

对应的net_bridge_fdb_entry结构:

//CAM表中对应的数据结构

struct net_bridge_fdb_entry

{

//用于CAM表连接的链表指针

struct hlist_nodehlist;

//此项对应的物理出口

struct net_bridge_port*dst;

union {

struct list_headage_list;

struct rcu_headrcu;

} u;

//此项的当前的引用计数

atomic_tuse_count;

//超时时间

unsigned longageing_timer;

//MAC地址

mac_addraddr;

//是否为主机地址

unsigned charis_local;

//是否为静态地址

unsigned charis_static;

};

struct net_bridge_port

{

//当前端口所在的briage

struct net_bridge*br;

//此端口对应的物理端口

struct net_device*dev;

//同一桥内的端口链表?

struct list_headlist;

/* STP */

u8priority;

u8state;

u16port_no;

unsigned chartopology_change_ack;

unsigned charconfig_pending;

port_idport_id;

port_iddesignated_port;

bridge_iddesignated_root;

bridge_iddesignated_bridge;

u32path_cost;

u32designated_cost;

struct timer_listforward_delay_timer;

struct timer_listhold_timer;

struct timer_listmessage_age_timer;

struct kobjectkobj;

struct rcu_headrcu;

};

桥模块中的结构太多,一一总结下,免的下次看

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值