20200102_jeffrey - silenceuncrio/diary GitHub Wiki

0840

review

0925

先來驗證禮拜二的猜想


理論上

我可以讓 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

image

0950

另一個待實驗的點

之前參考 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:

  1. 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  |
+-----------------+-----------------+-------+
  1. 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:
...

1330

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(+)

1410

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(+)

1540

繼續 gre tunnel keepalive

試著 inner IP header 和 outer IP header 都自己來

1705

目前搭配以下 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 送回來

⚠️ **GitHub.com Fallback** ⚠️