RIP(路由訊息協定)解析及Wireshark報文分析

路由訊息協定(RIP)是一種動態路由協定,它使用躍點數作為路由度量來搜尋源網路和目標網路之間的最佳路徑。而RIP協定封裝在UDP之上,連接埠為520,它是一種距離向量路由協定,具有AD值120,適用於OSI模型的應用層。

RIP協定有哪些版本

RIP是最古老的距離向量協定路由協定之一,於1980年代發明。它使用跳數(源和目標網路之間的路由器數量)作為度量標準,並且配置非常簡單。開發了兩種版本的協定,主要是RIPv1,RIPv2,RIPng

  • RIPv1


RIPv1僅支援有類路由協定,並且在路由更新中不發送子網掩碼。使用廣播每30秒發送一次完整的更新。

  • RIPv2


RIPv2 支援無類路由,並在路由更新中發送子網掩碼。

在RIP中的身份驗證中,路由訊息協定RIPv2的Cisco實現支援身份驗證,金鑰管理,路由匯總,無類域間路由(CIDR)和可變長度子網掩碼(VLSM)。

RIPv1不支援身份驗證。如果要發送和接收RIP v2資料包,則可以在介面上啟用RIP身份驗證。

RIP訊息格式

現在,我們看一下RIP訊息格式的結構。訊息格式用於在不同路由器之間共享訊息。RIP在訊息中包含以下欄位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| command (1)   | version (1)   |      must be zero (2)         |
+---------------+---------------+-------------------------------+
| address family identifier (2) |      must be zero (2)         |
+-------------------------------+-------------------------------+
|                         IP address (4)                        |
+---------------------------------------------------------------+
|                        must be zero (4)                       |
+---------------------------------------------------------------+
|                        must be zero (4)                       |
+---------------------------------------------------------------+
|                          metric (4)                           |
+---------------------------------------------------------------+

command :這是一個8位欄位,用於請求或答覆。請求的值為1,回復的值為2。

version :這裡的版本表示我們正在使用的協定版本。假設我們使用的是版本1的協定,然後將1放在此欄位中。

must be zero :這是一個保留欄位,因此用零填充。

address family identifier:這是一個16位欄位。由於我們使用的是TCP / IP系列,因此我們在此欄位中輸入2值。

IP address:定義為14個位元組的欄位。如果使用IPv4版本,則使用4個位元組,其他10個位元組全為零。

metric :距離欄位指定跳數,即到達目的地的跳數。

如何確定跳數?

當路由器將資料包發送到網段時,將其計為單跳。

在上圖中,當路由器1將資料包轉發到路由器2時,它將計為1跳計數。同樣,當路由器2將資料包轉發到路由器3時,它將計為2跳數,而當路由器3將資料包轉發到路由器4時,它將計為3跳數。

同樣,RIP最多可以支援15個躍點,這意味著可以在RIP中配置16個路由器。

RIP如何工作?

如果網路中有8個路由器,路由器1希望將資料發送到路由器3。如果網路配置了RIP,它將選擇跳數最少的路由。

上面的網路中有3條路由,即路由1,路由2和路由3。路由2包含的跳數最少,即2,其中路由1包含3跳,路由3包含4跳,因此RIP將選擇路線2。

RIP協定解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <net/ethernet.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define UDP_PORT_RIP    520

#define RIPv1   1
#define RIPv2   2

#define RIP_HEADER_LENGTH 4
#define RIP_ENTRY_LENGTH 20
#define MD5_AUTH_DATA_LEN 16

#define AFVAL_UNSPEC    0
#define AFVAL_IP        2

#define AUTH_IP_ROUTE           1
#define AUTH_PASSWORD           2
#define AUTH_KEYED_MSG_DIGEST   3

bool is_rip_version(uint8_t version)
{<!-- -->
    if (version == RIPv1 || version == RIPv2)
        return true;
    return false;
}

static void dissect_rip_authentication(u_char *data_info ,int offset)
{<!-- -->
    uint8_t auth_data_len = 0;
    char *pszInfoBuf = NULL;
    offset += 2;/*skip 0xFFFF*/
    uint16_t authtype = ntohs(*(uint16_t*)(data_info + offset));
    printf("authtype  0x%X
",authtype);
   
   
    switch(authtype)
    {<!-- -->
        case AUTH_PASSWORD:
       
            offset += 2; /*skip auth type*/
            pszInfoBuf = (char*)malloc(1024);
            if (pszInfoBuf != 0)
            {<!-- -->
                memcpy(pszInfoBuf, data_info + offset,16);
                pszInfoBuf[16] = '';
                printf("Password = %s
",pszInfoBuf);
            }  
            break;
           
        case AUTH_KEYED_MSG_DIGEST:
            offset += 2; /*authenication type*/
            printf("Digest Offset  %d
",ntohs(*(uint16_t*)(data_info + offset)));
            offset += 2; /*Digest Offset*/
            offset += 1; /*skip authenication type、Digest Offset、Key ID*/
            auth_data_len = *(uint8_t*)(data_info + offset);
            printf("Auth Data Len: %d
",auth_data_len);
           
            break;
        default:
            break;         
    }
   
    //return auth_data_len;
}

static void dissect_ip_rip_vektor(u_char *data_info, int offset, uint8_t version)
{<!-- -->
   
    uint32_t metric = 0;
   
    printf("Address Family %d
",ntohs(*(uint16_t*)(data_info + offset)));
    offset += 2; /*Address Family*/
     
    if (version == RIPv2)
    {<!-- -->
        printf("Route Tag %d
",ntohs(*(uint16_t*)(data_info + offset)));
       
    }
    offset += 2; /*Route Tag*/
    printf("IP Address %d.%d.%d.%d
",data_info[offset],data_info[offset +1],data_info[offset+2],data_info[offset+3]);
    offset += 4; /*IP Address*/
   
    if (version == RIPv2)
    {<!-- -->
        printf("Netmask: %d.%d.%d.%d
",data_info[offset],data_info[offset +1],data_info[offset+2],data_info[offset+3]);
        offset += 4; /*Netmask*/

        printf("Next Hop: %d.%d.%d.%d
",data_info[offset],data_info[offset +1],data_info[offset+2],data_info[offset+3]);
        offset += 4; /*Next Hop*/
       
        printf("Metric: %d
",ntohl(*(uint32_t*)(data_info + offset)));
    }

    if (version == RIPv1)
    {<!-- -->
        offset += 8;
        printf("Metric: %d
",ntohl(*(uint32_t*)(data_info + offset)));
       
    }

}

static void dissect_unspec_rip_vektor(u_char *data_info, int offset, uint8_t version)
{<!-- -->
    uint32_t metric = 0;
   
    printf("Address Family %d
",ntohs(*(uint16_t*)(data_info + offset)));
    offset += 2; /*Address Family*/
     
    if (version == RIPv2)
    {<!-- -->
        printf("Route Tag %d
",ntohs(*(uint16_t*)(data_info + offset)));
       
    }
    offset += 2; /*Route Tag*/

    if (version == RIPv2)
    {<!-- -->
        printf("Netmask: %d.%d.%d.%d
",data_info[offset],data_info[offset +1],data_info[offset+2],data_info[offset+3]);
        offset += 4; /*Netmask*/

        printf("Next Hop: %d.%d.%d.%d
",data_info[offset],data_info[offset +1],data_info[offset+2],data_info[offset+3]);
        offset += 4; /*Next Hop*/
        printf("Metric: %d
",ntohl(*(uint32_t*)(data_info + offset)));
    }
   
    if (version == RIPv1)
    {<!-- -->
        offset += 14;
        printf("Metric: %d
",ntohl(*(uint32_t*)(data_info + offset)));
       
    }
   
}


static void dissect_rip(u_char *data_info)
{<!-- -->
   
    int offset = 0;
    uint8_t command =  data_info[offset];
    uint8_t version = data_info[offset + 1];
    printf("version %d
",version);
    if ( !is_rip_version(version) )
        return;
   
    /* skip header */
    offset = RIP_HEADER_LENGTH;
   
    uint16_t  family = ntohs(*(uint16_t*)(data_info + offset));
    printf("family  0x%X
",family);
   
    switch(family)
    {<!-- -->
        case AFVAL_UNSPEC: /* Unspecified */
            dissect_unspec_rip_vektor(data_info,offset,version);
            break;
        case AFVAL_IP: /* IP */
                dissect_ip_rip_vektor(data_info,offset,version);
            break;
        case 0xFFFF:
               
            if (offset == RIP_HEADER_LENGTH)
            {<!-- -->
                dissect_rip_authentication(data_info,offset);
            }
            break;
        default:

            break;
           
    }
   
    offset += RIP_ENTRY_LENGTH;    
}


static void confirm_rip_packet(struct ip *pIp)
{<!-- -->
    int iIpTotalLen = ntohs(pIp->ip_len);
    int offset = 0;
    int nFragSeq = 0;
    struct udphdr* pUdpHdr = (struct udphdr*)((char*)pIp + (pIp->ip_hl<<2));
    if (pIp->ip_p == IPPROTO_UDP && (ntohs(pUdpHdr->dest) == UDP_PORT_RIP) || (ntohs(pUdpHdr->source) == UDP_PORT_RIP) )
    {<!-- -->
        printf("
");
        printf("info udp
");
       
        int iPayloadLen = iIpTotalLen - (pIp->ip_hl<<2) - 8;
        //printf("UDP Payload Len %d
", iPayloadLen);
       
        u_char *pRipHdr = (u_char*)(pUdpHdr+1);
        dissect_rip(pRipHdr );
       
    }  
}

編譯執行

RIPv1

RIPv2

總結

路由訊息在RIP網路中透過RIP請求和RIP回應資料包進行交換。剛剛啟動的路由器可以在所有啟用RIP的介面上廣播RIP請求。

在那些鏈路上執行RIP的任何路由器都會收到請求並透過立即向路由器發送RIP回應資料包來做出回應。

在沒有RIP請求資料包的情況下,所有RIP路由器每30秒在所有啟用RIP的介面上廣播RIP回應資料包。RIP廣播是在整個網路中泛洪拓撲訊息的主要方式。

參考:http:/ /www.ietf.org/rfc/rfc2082.txt

歡迎關注微信公眾號【程式猿編碼】,歡迎新增本人微訊號(17865354792),歡迎進入技術交流群。我們一起學習進步!