bgp 使用 tcp 连接,每个 bgp 实例自身是 peer 的一个 tcp server 端,同时也是 peer 的 tcp client 端。
1、在 bgp_create 之后都建立自己的 socket 服务端开始监听 179 端口:
- 1 bgp = bgp_create(as, name);
- 2 bgp_router_id_set(bgp, &router_id_zebra);
- 3 * bgp_val = bgp;
- 4 5
- /* Create BGP server socket, if first instance. */
- 6
- if (list_isempty(bm - >bgp) 7 && !bgp_option_check(BGP_OPT_NO_LISTEN)) {
- 8
- if (bgp_socket(bm - >port, bm - >address) < 0) return BGP_ERR_INVALID_VALUE;
- 9
- }
- 10 11 listnode_add(bm - >bgp, bgp);
bgp_socket 里完成 server socket 的创建与监听。
2、在 bgp_start 函数里开始对 peer 的 connect 操作。在 connect 之前会清楚一些 peer 的设置,避免与上一个连接 session 的混淆。
- 1 status = bgp_connect(peer);
如果定义了 HAVE_DECL_TCP_MD5SIG 宏,或者更老的 linux 2.4 内核版本的宏 HAVE_TCP_MD5_LINUX24,即会添加 TCP MD5 签名验证选项。
为了跟上时代的步伐,这里我们就只看高版本的内核了。
- 1 int keylen = password ? strlen(password) : 0;
- 2 struct tcp_md5sig md5sig;
- 3 union sockunion * su2,
- *susock;
- 4 5......6 7 memset( & md5sig, 0, sizeof(md5sig));
- 8 memcpy( & md5sig.tcpm_addr, su2, sizeof( * su2));
- 9 md5sig.tcpm_keylen = keylen;
- 10
- if (keylen) memcpy(md5sig.tcpm_key, password, keylen);
- 11 sockunion_free(susock);
- 12 13
- if ((ret = setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0) {
- 14
- /* ENOENT is harmless. It is returned when we clear a password for which
- 15 one was not previously set. */
- 16
- if (ENOENT == errno) ret = 0;
- 17
- else zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", 18 sock, safe_strerror(errno));
- 19
- }
- 20
- return ret;
上面的代码即完成 MD5SIG 选项。
如果 md5 值不正确或者密码错误,内核会丢弃当前的报文。
4.1.3 版本内核对 md5sig 的结构定义:
- 1 struct tcp_md5sig {
- 2 struct __kernel_sockaddr_storage tcpm_addr;
- /* address associated */
- 3 __u16 __tcpm_pad1;
- /* zero */
- 4 __u16 tcpm_keylen;
- /* key length */
- 5 __u32 __tcpm_pad2;
- /* zero */
- 6 __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN];
- /* key (binary) */
- 7
- };
在 int tcp_v4_rcv(struct sk_buff *skb) 函数里:
- 1 #ifdef CONFIG_TCP_MD5SIG
- 2 /*
- 3 * We really want to reject the packet as early as possible
- 4 * if:
- 5 * o We're expecting an MD5'd packet and this is no MD5 tcp option
- 6 * o There is an MD5 option and we're not expecting one
- 7 */
- 8 if (tcp_v4_inbound_md5_hash(sk, skb))
- 9 goto discard_and_relse;
- 10 #endif
因此在服务端,直接由内核在 tcp 接收处理时就完成了签名验证。
来源: