透過原始碼理解Rarp協議

大雄45發表於2020-09-26
導讀 rarp是透過mac地址查詢ip的協議,主要用於有mac的主機,但是沒有ip的情況。

透過原始碼理解Rarp協議透過原始碼理解Rarp協議

rarp協議的格式和arp協議是一樣的,他們都是透過一種地址查詢另外一種地址。作業系統內維護了一個轉換表。定義如下。

struct rarp_table 
{ 
    struct rarp_table  *next;             /* Linked entry list           */ 
    unsigned long      ip;                /* ip address of entry         */ 
    unsigned char      ha[MAX_ADDR_LEN];  /* Hardware address            */ 
    unsigned char      hlen;              /* Length of hardware address  */ 
    unsigned char      htype;             /* Type of hardware in use     */ 
    struct device      *dev;              /* Device the entry is tied to */ 
};

初始化的時候是空的,這個表格的資料來源於,使用者透過作業系統提供的介面設定。我們看如何操作這個表。

int rarp_ioctl(unsigned int cmd, void *arg) 
{ 
    struct arpreq r; 
    struct sockaddr_in *si; 
    int err; 
 
    switch(cmd) 
    { 
        case SIOCDRARP: 
            if (!suser()) 
                return -EPERM; 
            err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); 
            if(err) 
                return err; 
            memcpy_fromfs(&r, arg, sizeof(r)); 
            if (r.arp_pa.sa_family != AF_INET) 
                return -EPFNOSUPPORT; 
            si = (struct sockaddr_in *) &r.arp_pa; 
            rarp_destroy(si->sin_addr.s_addr); 
            return 0; 
 
        case SIOCGRARP: 
            err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); 
            if(err) 
                return err; 
            return rarp_req_get((struct arpreq *)arg); 
        case SIOCSRARP: 
            if (!suser()) 
                return -EPERM; 
            err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); 
            if(err) 
                return err; 
            return rarp_req_set((struct arpreq *)arg); 
        default: 
            return -EINVAL; 
    } 
 
    /*NOTREACHED*/ 
    return 0; 
}

透過ioctl函式,我們可以對錶格進行增刪改查。我們只關注新增的邏輯。因為其他的是類似的。下面是arpreq 的定義

struct arpreq { 
  struct sockaddr    arp_pa;        /* protocol address        */ 
  struct sockaddr    arp_ha;        /* hardware address        */ 
  int            arp_flags;    /* flags            */ 
  struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */ 
};
static int rarp_req_set(struct arpreq *req) 
{ 
    struct arpreq r; 
    struct rarp_table *entry; 
    struct sockaddr_in *si; 
    int htype, hlen; 
    unsigned long ip; 
    struct rtable *rt; 
 
    memcpy_fromfs(&r, req, sizeof(r)); 
 
    /* 
     *    We only understand about IP addresses...  
     */ 
 
    if (r.arp_pa.sa_family != AF_INET) 
        return -EPFNOSUPPORT; 
 
    switch (r.arp_ha.sa_family)  
    { 
        case ARPHRD_ETHER: 
            htype = ARPHRD_ETHER; 
            hlen = ETH_ALEN; 
            break; 
        default: 
            return -EPFNOSUPPORT; 
    } 
 
    si = (struct sockaddr_in *) &r.arp_pa; 
    ip = si->sin_addr.s_addr; 
    if (ip == 0) 
    { 
        printk("RARP: SETRARP: requested PA is 0.0.0.0 !\n"); 
        return -EINVAL; 
    } 
    //  
    rt = ip_rt_route(ip, NULL, NULL); 
    if (rt == NULL) 
        return -ENETUNREACH; 
 
/* 
 *    Is there an existing entry for this address?  Find out... 
 */ 
 
    cli(); 
    // 判斷之前是不是已經存在 
    for (entry = rarp_tables; entry != NULL; entry = entry->next) 
        if (entry->ip == ip) 
            break; 
 
/* 
 *    If no entry was found, create a new one. 
 */ 
    // 不存在則建立一個表項 
    if (entry == NULL) 
    { 
        entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table), 
                    GFP_ATOMIC); 
        // 還沒初始化則初始化 
        if(initflag) 
        { 
            rarp_init(); 
            initflag=0; 
        } 
 
        entry->next = rarp_tables; 
        rarp_tables = entry; 
    } 
 
    entry->ip = ip; 
    entry->hlen = hlen; 
    entry->htype = htype; 
    memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); 
    entry->dev = rt->rt_dev; 
 
    sti(); 
 
    return 0; 
}

我們看到這裡會往表裡插入一個表項(如果不存在的話),還有另外一個邏輯是rarp_init。

static void rarp_init (void) 
{ 
    /* Register the packet type */ 
    rarp_packet_type.type=htons(ETH_P_RARP); 
    dev_add_pack(&rarp_packet_type); 
}

這個函式是往底層註冊一個節點,當mac底層收到一個ETH_P_RARP型別的資料包的時候(在mac協議頭裡定義),就會執行rarp_packet_type中定義的函式。下面是該rarp_packet_type的定義

static struct packet_type rarp_packet_type = 
{ 
    0,  
    0,                /* copy */ 
    rarp_rcv, 
    NULL, 
    NULL 
};

rarp_rcv函式就是收到一個rarp請求的時候(來自其他主機),執行的函式。

int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 
{ 
/* 
 *    We shouldn't use this type conversion. Check later. 
 */ 
    // rarp協議報文 
    struct arphdr *rarp = (struct arphdr *)skb->h.raw; 
    // rarp協議資料部分 
    unsigned char *rarp_ptr = (unsigned char *)(rarp+1); 
    struct rarp_table *entry; 
    long sip,tip; 
    unsigned char *sha,*tha;            /* s for "source", t for "target" */ 
 
    // 硬體地址長度或型別不一致則忽略 
    if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)  
        || dev->flags&IFF_NOARP) 
    { 
        kfree_skb(skb, FREE_READ); 
        return 0; 
    } 
 
    /* 
     *    If it's not a RARP request, delete it. 
     */ 
    // 不是請求報文則忽略 
    if (rarp->ar_op != htons(ARPOP_RREQUEST)) 
    { 
        kfree_skb(skb, FREE_READ); 
        return 0; 
    } 
    /* 
     *    Extract variable width fields 
     */ 
    // rarp協議首地址 
    sha=rarp_ptr; 
    // 傳送端mac地址長度 
    rarp_ptr+=dev->addr_len; 
    // 拿到傳送端ip,存到sip 
    memcpy(&sip,rarp_ptr,4); 
    // 跳過4位元組 
    rarp_ptr+=4; 
    // 目的mac地址 
    tha=rarp_ptr; 
    // 跳過mac地址長度 
    rarp_ptr+=dev->addr_len; 
    // 目的ip地址 
    memcpy(&tip,rarp_ptr,4); 
 
    /* 
     *    Process entry. Use tha for table lookup according to RFC903. 
     */ 
 
    cli(); 
    for (entry = rarp_tables; entry != NULL; entry = entry->next) 
        // 判斷mac地址是否相等 
        if (!memcmp(entry->ha, tha, rarp->ar_hln)) 
            break; 
    // 非空則說明找到 
    if (entry != NULL) 
    {    // 拿到對應的ip 
        sip=entry->ip; 
        sti(); 
        // 回覆,類似是響應ARPOP_RREPLY 
        arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha,  
            dev->dev_addr); 
    } 
    else 
        sti(); 
 
    kfree_skb(skb, FREE_READ); 
    return 0; 
}

我們看到這個函式很長,不過邏輯比較簡單,就是解析收到的rarp請求中的資料,然後根據其他主機請求的mac地址,從維護的表格中找到對應的ip(如果有的話),然後呼叫arp_send函式傳送回包。下面列一下該函式的程式碼。

void arp_send(int type, int ptype, unsigned long dest_ip,  
          struct device *dev, unsigned long src_ip,  
          unsigned char *dest_hw, unsigned char *src_hw) 
{ 
    struct sk_buff *skb; 
    struct arphdr *arp; 
    unsigned char *arp_ptr; 
 
    /* 
     *    No arp on this interface. 
     */ 
 
    if(dev->flags&IFF_NOARP) 
        return; 
 
    /* 
     *    Allocate a buffer 
     */ 
    // 分配一個skb儲存資料包 
    skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4) 
                + dev->hard_header_len, GFP_ATOMIC); 
    // 構造arp協議資料包 
    skb->len = sizeof(struct arphdr) + dev->hard_header_len + 2*(dev->addr_len+4); 
    skb->arp = 1; 
    skb->dev = dev; 
    // 不存在快取,發完可以銷燬 
    skb->free = 1; 
    // 構造mac頭 
    dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb); 
 
    /* Fill out the arp protocol part. */ 
    arp = (struct arphdr *) (skb->data + dev->hard_header_len); 
    arp->ar_hrd = htons(dev->type); 
    arp->ar_pro = htons(ETH_P_IP); 
    arp->ar_hln = dev->addr_len; 
    arp->ar_pln = 4; 
    arp->ar_op = htons(type); 
    arp_ptr=(unsigned char *)(arp+1); 
    memcpy(arp_ptr, src_hw, dev->addr_len); 
    arp_ptr+=dev->addr_len; 
    memcpy(arp_ptr, &src_ip,4); 
    arp_ptr+=4; 
    if (dest_hw != NULL) 
        memcpy(arp_ptr, dest_hw, dev->addr_len); 
    else 
        memset(arp_ptr, 0, dev->addr_len); 
    arp_ptr+=dev->addr_len; 
    memcpy(arp_ptr, &dest_ip, 4); 
    // 呼叫mac頭髮送函式傳送出去 
    dev_queue_xmit(skb, dev, 0); 
}

這就是rarp的早期實現。

原文來自: 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2724352/,如需轉載,請註明出處,否則將追究法律責任。

相關文章