• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Linux網橋源碼框架分析初步

    發表于:2007-05-26來源:作者:點擊數: 標簽:
    今天處理網橋的STP的問題遇到了麻煩,對這個東東理論的倒是看了不少,沒有真真學習到它的源理,來看 Linux 的實現,手頭沒有資料,看了兩個鐘頭,只把網橋的框架結構看完,所以想先貼出來,希望有研究這塊的大哥們討論,繼續把它寫完,九賤好學習一下: 版本
     今天處理網橋的STP的問題遇到了麻煩,對這個東東理論的倒是看了不少,沒有真真學習到它的源理,來看Linux的實現,手頭沒有資料,看了兩個鐘頭,只把網橋的框架結構看完,所以想先貼出來,希望有研究這塊的大哥們討論,繼續把它寫完,九賤好學習一下:

    版本:Linux 2.4.18

    一、調用
    在src/net/core/dev.c的軟中斷函數static void net_rx_action(struct softirq_action *h)中:
    line 1479

    #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
    if (skb->dev->br_port != NULL &&
    br_handle_frame_hook != NULL) {
    handle_bridge(skb, pt_prev);
    dev_put(rx_dev);
    continue;
    }
    #endif
    如果定義了網橋或網橋模塊,則由handle_bridge函數處理
    skb->dev->br_port :接收該數據包的端口是網橋端口組的一員
    br_handle_frame_hook :定義了網橋處理函數

    二、初始化
    src/net/bridge/br.c:
    static int __init br_init(void)
    {
    printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n";

    br_handle_frame_hook = br_handle_frame;
    br_ioctl_hook = br_ioctl_deviceless_stub;
    #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
    br_fdb_get_hook = br_fdb_get;
    br_fdb_put_hook = br_fdb_put;
    #endif
    register_netdevice_notifier(&br_device_notifier);

    return 0;
    }
    初始化函數指明了網橋的處理函數是br_handle_frame
    ioctl處理函數是:br_ioctl_deviceless_stub

    三、br_handle_frame(br_input.c)
    /*網橋處理函數*/
    void br_handle_frame(struct sk_buff *skb)
    {
    struct net_bridge *br;
    unsigned char *dest;
    struct net_bridge_port *p;

    /*獲取目的MAC地址*/
    dest = skb->mac.ethernet->h_dest;

    /*skb->dev->br_port用于指定接收該數據包的端口,若不是屬于網橋的端口,則為NULL*/
    p = skb->dev->br_port;
    if (p == NULL) /*端口不是網橋組端口中*/
    goto err_nolock;

    /*本端口所屬的網橋組*/
    br = p->br;

    /*加鎖,因為在轉發中需要讀CAM表,所以必須加讀鎖,避免在這個過程中另外的內核控制路徑(如多處理機上另外一個CPU上的系統調用)修改CAM表*/
    read_lock(&br->lock);
    if (skb->dev->br_port == NULL) /*前面判斷過的*/
    goto err;

    /*br->dev是網橋的虛擬網卡,如果它未UP,或網橋DISABLED,p->state實際上是橋的當前端口的STP計算判斷后的狀態*/
    if (!(br->dev.flags & IFF_UP) ||
    p->state == BR_STATE_DISABLED)
    goto err;

    /*源MAC地址為255.X.X.X,即源MAC是多播或廣播,丟棄之*/
    if (skb->mac.ethernet->h_source[0] & 1)
    goto err;

    /*眾所周之,網橋之所以是網橋,比HUB更智能,是因為它有一個MAC-PORT的表,這樣轉發數據就不用廣播,而查表定端口就可以了
    每次收到一個包,網橋都會學習其來源MAC,添加進這個表。Linux中這個表叫CAM表(這個名字是其它資料上看的)。
    如果橋的狀態是LEARNING或FORWARDING(學習或轉發),則學習該包的源地址skb->mac.ethernet->h_source,
    將其添加到CAM表中,如果已經存在于表中了,則更新定時器,br_fdb_insert完成了這一過程*/
    if (p->state == BR_STATE_LEARNING ||
    p->state == BR_STATE_FORWARDING)
    br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);

    /*STP協議的BPDU包的目的MAC采用的是多播目標MAC地址:從01-80-c2-00-00-00(Bridge_group_addr:網橋組多播地址)開始
    所以這里是如果開啟了STP,而當前數據包又是一個BPDU
    (!memcmp(dest, bridge_ula, 5), unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },
    則交由相應函數處理*/
    if (br->stp_enabled &&
    /* 這里只比較前5個字節,沒有仔細研究過STP是使用了全部多播地址(從0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。),還是只使用了一部份,這里看來似乎只是一部份,沒去深究了*/
    !memcmp(dest, bridge_ula, 5) &&
    !(dest[5] & 0xF0)) /*01-80-c2-00-00-F0 是一個什么地址?為什么要判斷呢?*/
    goto handle_special_frame;

    /*處理鉤子函數,然后轉交br_handle_frame_finish函數繼續處理*/
    if (p->state == BR_STATE_FORWARDING) {
    NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
    br_handle_frame_finish);
    read_unlock(&br->lock);
    return;
    }

    err:
    read_unlock(&br->lock);
    err_nolock:
    kfree_skb(skb);
    return;

    handle_special_frame:
    if (!dest[5]) {
    br_stp_handle_bpdu(skb);
    return;
    }

    kfree_skb(skb);
    }

    四、br_handle_frame_finish

    static int br_handle_frame_finish(struct sk_buff *skb)
    {
    struct net_bridge *br;
    unsigned char *dest;
    struct net_bridge_fdb_entry *dst;
    struct net_bridge_port *p;
    int passedup;

    /*前面基本相同*/
    dest = skb->mac.ethernet->h_dest;


    p = skb->dev->br_port;
    if (p == NULL)
    goto err_nolock;

    br = p->br;
    read_lock(&br->lock);
    if (skb->dev->br_port == NULL)
    goto err;

    passedup = 0;

    /*如果網橋的虛擬網卡處于混雜模式,那么每個接收到的數據包都需要克隆一份
    送到AF_PACKET協議處理體(網絡軟中斷函數net_rx_action中ptype_all鏈的處理)。*/
    if (br->dev.flags & IFF_PROMISC) {
    struct sk_buff *skb2;

    skb2 = skb_clone(skb, GFP_ATOMIC);
    if (skb2 != NULL) {
    passedup = 1;
    br_pass_frame_up(br, skb2);
    }
    }

    /*目的MAC為廣播或多播,則需要向本機的上層協議棧傳送這個數據包,這里有一個標志變量passedup
    用于表示是否傳送過了,如果已傳送過,那就算了*/
    if (dest[0] & 1) {
    br_flood_forward(br, skb, !passedup);
    if (!passedup)
    br_pass_frame_up(br, skb);
    goto out;
    }

    /*Linux中的MAC-PORT表是CAM表,這里根據目的地址來查表,以確定由哪個接口把包轉發出去
    每一個表項是通過結構struct net_bridge_fdb_entry來描述的:
    struct net_bridge_fdb_entry
    {
    struct net_bridge_fdb_entry *next_hash; //用于CAM表連接的鏈表指針
    struct net_bridge_fdb_entry **pprev_hash; //為什么是pprev不是prev呢?還沒有仔細去研究
    atomic_t use_count; //此項當前的引用計數器
    mac_addr addr; //MAC地址
    struct net_bridge_port *dst; //此項所對應的物理端口
    unsigned long ageing_timer; //處理MAC超時
    unsigned is_local:1; //是否是本機的MAC地址
    unsigned is_static:1; //是否是靜態MAC地址
    };*/
    dst = br_fdb_get(br, dest);

    /*查詢CAM表后,如果能夠找到表項,并且目的MAC是到本機的虛擬網卡的,那么就需要把這個包提交給上層協議,
    這樣,我們就可以通過這個虛擬網卡的地址來遠程管理網橋了*/
    if (dst != NULL && dst->is_local) {
    if (!passedup)
    br_pass_frame_up(br, skb);
    else
    kfree_skb(skb);
    br_fdb_put(dst);
    goto out;
    }

    /*查到表了,且不是本地虛擬網卡的,轉發之*/
    if (dst != NULL) {
    br_forward(dst->dst, skb);
    br_fdb_put(dst);
    goto out;
    }

    /*如果表里邊查不到,那么只好學習學習HUB了……*/
    br_flood_forward(br, skb, 0);

    out:
    read_unlock(&br->lock);
    return 0;

    err:
    read_unlock(&br->lock);
    err_nolock:
    kfree_skb(skb);
    return 0;
    }

    基本框架就是這樣了,與那些講網橋原理的書上講的基本差不多……
    網橋之所以是網橋,主要靠這兩個函數:
    br_fdb_insert
    br_fdb_get
    一個學習,一個查表;
    另外,支持STP,處理BPDU,需要用到函數br_stp_handle_bpdu
    哪位有這三個函數的細節分析,可否送九賤一份,免得下午那么辛苦再去啃代碼……

    掃了一下 br_fdb_insert,結構還是很清析,如果當前項已存在于hash表項中,則更新它(__fdb_possibly_replace),如果是新項,則插入,實際是一個雙向鏈表的維護過程(__hash_link):

    void br_fdb_insert(struct net_bridge *br,
    struct net_bridge_port *source,
    unsigned char *addr,
    int is_local)
    {
    struct net_bridge_fdb_entry *fdb;
    int hash;

    hash = br_mac_hash(addr);

    write_lock_bh(&br->hash_lock);
    fdb = br->hash[hash];
    while (fdb != NULL) {
    if (!fdb->is_local &&
    !memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
    __fdb_possibly_replace(fdb, source, is_local);
    write_unlock_bh(&br->hash_lock);
    return;
    }

    fdb = fdb->next_hash;
    }

    fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
    if (fdb == NULL) {
    write_unlock_bh(&br->hash_lock);
    return;
    }

    memcpy(fdb->addr.addr, addr, ETH_ALEN);
    atomic_set(&fdb->use_count, 1);
    fdb->dst = source;
    fdb->is_local = is_local;
    fdb->is_static = is_local;
    fdb->ageing_timer = jiffies;

    __hash_link(br, fdb, hash);

    write_unlock_bh(&br->hash_lock);
    }

    同樣,查表也是一個遍歷鏈表,進行地址匹配的過程:
    struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
    {
    struct net_bridge_fdb_entry *fdb;

    read_lock_bh(&br->hash_lock);
    fdb = br->hash[br_mac_hash(addr)];
    while (fdb != NULL) {
    if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
    if (!has_expired(br, fdb)) {
    atomic_inc(&fdb->use_count);
    read_unlock_bh(&br->hash_lock);
    return fdb;
    }

    read_unlock_bh(&br->hash_lock);
    return NULL;
    }

    fdb = fdb->next_hash;
    }

    read_unlock_bh(&br->hash_lock);
    return NULL;
    }

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>