20200102_jeffrey - silenceuncrio/diary GitHub Wiki
review
先來驗證禮拜二的猜想
理論上
我可以讓 m330 - 192.168.1.11 送 gre tunnel keepalive request 給 m330 - 192.168.1.11
然後 m330 - 192.168.1.11 解開後會把 gre tunnel keepalive responce 送給 PC - 192.168.1.113
這需要先修改 m330 - 192.168.1.11 上的 gre1 設定
搭配以下的環境
+-------+ +-------+
| | lan | |
| m330 +-------------------+-----------------------+ m330 |
| | 192.168.1.11 | 192.168.1.13 | |
+-------+ | +-------+
|
+---+-------+ +-------+
| | 192.168.1.113 | |
| switch +---------------+ PC |
| | | |
+-----------+ +-------+
gre 設定
m330 - 192.168.1.11
ip tunnel add gre1 mode gre local 192.168.1.11 remote 192.168.1.13 ttl 255
ip link set gre1 up
ip addr add 10.0.0.11/24 dev gre1
m330 - 192.168.1.13
ip tunnel add gre1 mode gre local 192.168.1.13 remote 192.168.1.11 ttl 255
ip link set gre1 up
ip addr add 10.0.0.13/24 dev gre1
m330 - 192.168.1.11 - gre_keepalive_04.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <linux/ip.h>
#include <linux/if.h>
struct grehdr {
u_int16_t flags;
u_int16_t protocol;
} __packed;
int main(int argc, char *argv[])
{
int sd;
if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) {
printf("socket() failed!\n");
return(1);
}
printf("OK: a raw socket with GRE protocol is created.\n");
// inform the kernel do not fill up the packet structure, we will build our own
int one = 1;
const int *val = &one;
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
perror("setsockopt() error");
exit(2);
}
printf("OK: socket option IP_HDRINCL is set.\n");
// bind raw socket to specific interface
const char *opt;
opt = "gre1";
const len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMSIZ) {
fprintf(stderr, "Too long iface name");
return 1;
}
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, len);
printf("OK: socket option SO_BINDTODEVICE is set.\n");
struct sockaddr_in dest;
dest.sin_family = AF_INET;
if (inet_pton(AF_INET, "192.168.1.13", &(dest.sin_addr)) != 1) {
printf("Bad Address!\n");
return(1);
}
char buffer[64];
// fabricate the IP header
struct iphdr *ip = (struct iphdr *) buffer;
ip->ihl = 5;
ip->version = 4;
ip->tos = 16; // low delay
ip->tot_len = sizeof(struct iphdr) + sizeof(struct grehdr);
ip->id = htons(0);
ip->ttl = 64; // hops
ip->protocol = 47; // GRE
inet_pton(AF_INET, "192.168.1.13", &(ip->saddr));
inet_pton(AF_INET, "192.168.1.113", &(ip->daddr));
// fabricate the GRE header
struct grehdr *greh = (struct grehdr *) (buffer + sizeof(struct iphdr));
memset(greh, 0, sizeof(struct grehdr));
greh->protocol = 0;
if (sendto(sd, &buffer, ip->tot_len, 0, (struct sockaddr*) &dest, sizeof(struct sockaddr)) < 0) {
printf("sendto() failed!\n");
return(1);
}
return(0);
}
PC 端沒收到 GRE packet !!!
想到之前 john 提過的 accept_local
m330 - 192.168.1.13
root@(none):/tmp# cat /proc/sys/net/ipv4/conf/gre1/accept_local
0
root@(none):/tmp# echo 1 > /proc/sys/net/ipv4/conf/gre1/accept_local
root@(none):/tmp# cat /proc/sys/net/ipv4/conf/gre1/accept_local
1
PC 端收到 GRE packet - 正是 gre tunnel keepalive response
另一個待實驗的點
之前參考 cisco 寫的 wiki - gre tunnel keepalive
裡頭有提到
+-------------------+ +--------+ +-------------------+
| | | | | |
| Router A +--------+ ISP +--------+ Router B |
| | | | | |
+-------------------+ +--------+ +-------------------+
Tu0 - 10.10.10.1/28 Tu0 - 10.10.10.2/28
L0 - 192.168.1.1/32 L0 - 192.168.1.1/32
In this scenario, Router A performs these steps:
- Constructs the inner IP header every five seconds where:
- the source is set as the local the tunnel destination, which is 192.168.1.2
- the destination is set as the local tunnel source, which is 192.168.1.1
and a GRE header is added with a Protocol Type (PT) of 0
Packet generated by Router A but not sent:
+-----------------------------------+-------+
| GRE IP Header | GRE |
+-----------------+-----------------+-------+
| src:192.168.1.2 | dst:192.168.1.1 | PT=0 |
+-----------------+-----------------+-------+
- Sends that packet out of its tunnel interface, which results in the encapsulation of the packet with the outer IP header where:
- the source is set as the local the tunnel source, which is 192.168.1.1
- the destination is set as the local tunnel destination, which is 192.168.1.2
and a GRE header is added with PT = IP.
Packet sent from Router A to Router B:
+-----------------------------------+-------+---------------------------------------------------+
| GRE IP Header | GRE | +-------------------------------------+-------+ |
| | | | IP Header | GRE | |
+-----------------+-----------------+-------+ +------------------+------------------+-------+ |
| src:192.168.1.1 | dst:192.168.1.2 | PT=IP | | src: 192.168.1.2 | dst: 192.168.1.1 | PT=0 | |
| | | | +------------------+------------------+-------+ |
+-----------------+-----------------+-------+---------------------------------------------------+
Sends that packet out of its tunnel interface ???
以 m330 - 192.168.1.11 而言
我可能視情況需要控制 gre1 的 up/down
問題是在 down 的狀況下
我還能夠透過該 interface send packet 出去嗎? 畢竟 m330 用的是 linux
趕緊做實驗 - 利用 ip link set gre1 down
將 gre1 改成 down
root@(none):/tmp# ifconfig gre1
gre1 Link encap:UNSPEC HWaddr C0-A8-01-0B-CF-2B-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.0.11 P-t-P:10.0.0.11 Mask:255.255.255.0
inet6 addr: fe80::5efe:c0a8:10b/64 Scope:Link
UP POINTOPOINT RUNNING NOARP MTU:1476 Metric:1
RX packets:3 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:252 (252.0 B) TX bytes:324 (324.0 B)
root@(none):/tmp# ip link set gre1 down
root@(none):/tmp# ifconfig gre1
gre1 Link encap:UNSPEC HWaddr C0-A8-01-0B-7F-2B-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.0.11 P-t-P:10.0.0.11 Mask:255.255.255.0
POINTOPOINT NOARP MTU:1476 Metric:1
RX packets:3 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:252 (252.0 B) TX bytes:324 (324.0 B)
一樣的環境
+-------+ +-------+
| | lan | |
| m330 +-------------------+-----------------------+ m330 |
| | 192.168.1.11 | 192.168.1.13 | |
+-------+ | +-------+
|
+---+-------+ +-------+
| | 192.168.1.113 | |
| switch +---------------+ PC |
| | | |
+-----------+ +-------+
一樣的 m330 - 192.168.1.11 - gre_keepalive_04.c
root@(none):/tmp# ./grek_04
OK: a raw socket with GRE protocol is created.
OK: socket option IP_HDRINCL is set.
OK: socket option SO_BINDTODEVICE is set.
sendto() failed!
唉! 連送都送不出去
看來 m330 並無法像 cisco 的文件所描述的
1. Constructs the inner IP header every five seconds where:
...
2. Sends that packet out of its tunnel interface, which results in the encapsulation of the packet with the outer IP header where:
...
m330[release/v0.08] 小修改
m330[release/v0.08] - new field 'Service Name' at 'PPPoE Client Configuration' sectiion of 'WAN > Ethernet' web page
commit ad6fe1570a215cb4602e2b771ca6588b5067a4ce
Refs: [release/v0.08], {origin/release/v0.08}
Author: jeffrey <[email protected]>
Date: Thu Jan 2 13:27:01 2020 +0800
new field 'Service Name' at 'PPPoE Client Configuration' section of 'WAN > Ethernet' web page
proscend/prosrc/www/app/feature/wanEthernet.html | 8 ++++++++
proscend/prosrc/www/app/locale-en.json | 1 +
proscend/prosrc/www/app/locale-fr.json | 1 +
proscend/prosrc/www/app/locale-zh-tw.json | 1 +
4 files changed, 11 insertions(+)
m330[release/v0.08] - add 'szServiceName' into apply of pppoe.cgi
commit 5ccdbf3ec1943d844d96132f3a9f5f55097228cd
Refs: [release/v0.08], {origin/release/v0.08}
Author: jeffrey <[email protected]>
Date: Thu Jan 2 13:40:07 2020 +0800
add 'szServiceName' into apply of pppoe.cgi
proscend/prosrc/webcgi/pppoe.c | 2 ++
1 file changed, 2 insertions(+)
m330[release/v0.08] - add 'szServiceName' into set and apply logic of pppoe.cgi
commit 73ea0af6455cc098e914fbbb64720c9c6d029f28
Refs: [release/v0.08], {origin/release/v0.08}
Author: jeffrey <[email protected]>
Date: Thu Jan 2 13:44:32 2020 +0800
add 'szServiceName' into set and apply logic of pppoe.cgi
proscend/prosrc/webcgi/pppoe.c | 2 ++
1 file changed, 2 insertions(+)
m330[release/v0.08] - new field 'Service Name' at 'PPPoE Client Configuration' section also for 'setting wizard' web page
commit 8e3ed32081b97e7695007b60dd9e3101c05256b6
Refs: [release/v0.08], {origin/release/v0.08}
Author: jeffrey <[email protected]>
Date: Thu Jan 2 14:08:18 2020 +0800
new field 'Service Name' at 'PPPoE Client Configuration' section also for 'setting wizard' web page
proscend/prosrc/www/app/feature/wizard.html | 10 ++++++++++
1 file changed, 10 insertions(+)
繼續 gre tunnel keepalive
試著 inner IP header 和 outer IP header 都自己來
目前搭配以下 topology
+-------+ +-------+
| | lan | |
| m330 +-------------------------------------------+ m330 |
| | 192.168.1.11 192.168.1.13 | |
+-------+ +-------+
gre 設定
m330 - 192.168.1.11
ip tunnel add gre1 mode gre local 192.168.1.11 remote 192.168.1.13 ttl 255
ip link set gre1 up
ip addr add 10.0.0.11/24 dev gre1
m330 - 192.168.1.13
ip tunnel add gre1 mode gre local 192.168.1.13 remote 192.168.1.11 ttl 255
ip link set gre1 up
ip addr add 10.0.0.13/24 dev gre1
記得設定 accept_local 為 1 - 這樣才會送回 gre tunnel response
m330 - 192.168.1.13
root@(none):/tmp# cat /proc/sys/net/ipv4/conf/gre1/accept_local
0
root@(none):/tmp# echo 1 > /proc/sys/net/ipv4/conf/gre1/accept_local
root@(none):/tmp# cat /proc/sys/net/ipv4/conf/gre1/accept_local
1
m330 - 192.168.1.11 - gre_keepalive_05.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <linux/ip.h>
#include <linux/if.h>
struct grehdr {
u_int16_t flags;
u_int16_t protocol;
} __packed;
unsigned short csum(unsigned short *ptr,int nbytes) {
long sum;
unsigned short oddbyte;
short answer;
//Debug info
//hexdump((unsigned char *) ptr, nbytes);
//printf("csum nbytes: %d\n", nbytes);
//printf("csum ptr address: %p\n", ptr);
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
int main(int argc, char *argv[])
{
int sd;
if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_GRE)) < 0) {
printf("socket() failed!\n");
return(1);
}
printf("OK: a raw socket with GRE protocol is created.\n");
#if 0
// inform the kernel do not fill up the packet structure, we will build our own
int one = 1;
const int *val = &one;
if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
perror("setsockopt() error");
exit(2);
}
printf("OK: socket option IP_HDRINCL is set.\n");
#endif
#if 0
// bind raw socket to specific interface
const char *opt;
opt = "gre1";
const len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMSIZ) {
fprintf(stderr, "Too long iface name");
return 1;
}
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, len);
printf("OK: socket option SO_BINDTODEVICE is set.\n");
#endif
struct sockaddr_in dest;
dest.sin_family = AF_INET;
if (inet_pton(AF_INET, "192.168.1.13", &(dest.sin_addr)) != 1) {
printf("Bad Address!\n");
return(1);
}
char buffer[128];
// let kernel decide the outer IP header
// fabricate the outer GRE header
struct grehdr *outer_greh = (struct grehdr *) buffer;
memset(outer_greh, 0, sizeof(struct grehdr));
outer_greh->protocol = 0x0800; // IP
// fabricate the inner IP header
struct iphdr *inner_ip = (struct iphdr *) (buffer + sizeof(struct grehdr));
memset(inner_ip, 0, sizeof(struct iphdr));
inner_ip->ihl = 5; // the number of 32-bit words in the header
inner_ip->version = 4; // IPv4
inner_ip->tos = 16; // low delay
inner_ip->tot_len = sizeof(struct iphdr) + sizeof(struct grehdr);
inner_ip->id = htons(0);
inner_ip->frag_off = 0x00;
inner_ip->ttl = 64;
inner_ip->protocol = 47; // GRE
inner_ip->check = 0; //16 bit checksum of IP header. Can't calculate at this point
inet_pton(AF_INET, "192.168.1.13", &(inner_ip->saddr));
inet_pton(AF_INET, "192.168.1.11", &(inner_ip->daddr));
// fabricate the inner GRE header
struct grehdr *inner_greh = (struct grehdr *) (buffer + sizeof(struct grehdr) + sizeof(struct iphdr));
memset(inner_greh, 0, sizeof(struct grehdr));
inner_greh->protocol = htons(0); // defined at cisco tunnel keepalive
// calculate the checksum for integrity
inner_ip->check = csum((unsigned short *) (buffer + sizeof(struct grehdr)), sizeof(struct iphdr) + sizeof(struct grehdr));
printf("checksum: 0x%04x\n", inner_ip->check);
if (sendto(sd, &buffer, sizeof(struct grehdr) + sizeof(struct iphdr) + sizeof(struct grehdr), 0,
(struct sockaddr*) &dest, sizeof(struct sockaddr)) < 0) {
printf("sendto() failed!\n");
return(1);
}
return(0);
}
已經確認不管 gre1 是 up 或 down
gre tunnel keepalive request 都能正常發送
而且也能收到 gre tunnel keepalive 的 response
root@(none):/tmp# tcpdump -X -s0 -i eth0 &
root@(none):/tmp# tcpdump -X -s0 -i eth0 &tcpdump: WARNING: eth0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
root@(none):/tmp# ./grek_05
OK: a raw socket with GRE protocol is created.
checksum: 0xf73e
09:00:51.277559 IP 192.168.1.11 > 192.168.1.13: GREv0, length 28: IP 192.168.1.13 > 192.168.1.11: GREv0, length 4: gre-proto-0x0
0x0000: 4500 0030 0000 4000 402f b736 c0a8 010b E..0..@.@/.6....
0x0010: c0a8 010d 0000 0800 4510 0018 0000 0000 ........E.......
0x0020: 402f f73e c0a8 010d c0a8 010b 0000 0000 @/.>............
09:00:51.277791 IP 192.168.1.13 > 192.168.1.11: GREv0, length 4: gre-proto-0x0
root@(none):/tmp# 0x0000: 4510 0018 0000 0000 3f2f f83e c0a8 010d E.......?/.>....
0x0010: c0a8 010b 0000 0000 0000 0000 0000 0000 ................
0x0020: 0000 0000 0000 0000 0000 0000 0000 ..............
...
root@(none):/tmp# killall tcpdump
感謝 raw_tcp_socket.c 的作者
之前參考的 ip checksum 算法有誤
導致遠端解開 gre 後並沒有把 inner 的 gre tunnel keepalive response 送回來