« UnixBenchの中身を見てみるよ | ホーム | ひゃくめがしょっく »

2010年5月14日

複数インタフェイスを持つサーバ上でxl2tpdを動かしたときにね。

例のLinuxルータ(nelly)、NICが2つ付いてる訳なんですけど、っていうかpppoeかましてしかもマルチセッションしてるからインタフェイスの数がまあ5-6個くらいはある形になってるんです。で、ppp0でOCN、ppp1でwakwakっていうマルチセッションをやってて、wakwakの方が固定IPアドレス付けてまして、っていう構成なんですがね。

そこに外からVPNを掛けたいとしましょうや。VPNの受け口にはまあOCNは非固定アドレスなんでDynamic DNSでもやってない限り使いにくい。なんでwakwakの方でVPNの受信をさせようかとか思うとですね、L2TP+IPSECでやってるとxl2tpdがよろしくないんです。IPsecのネゴシエートが終わってL2TPに処理が移るとですね、xl2tpdさん何を血迷ったか、ppp1経由で受け取ったはずのL2TPクライアントからのリクエストに対して、default routeになってるppp0経由でレスポンス返そうとするんですわ。したらあんさん、L2TPクライアントからすればリクエスト送ったはずのサーバアドレス(=ppp1)と違うアドレス(=ppp0)からレスポンスが返ってきたって知るかーってなもんですよ。いやー、参りましたわー。orz

というわけでこれ、直しました。受信IPアドレスを使って打ち返すように、sendmsg()でmsg_controlにIP_PKTINFOを仕込んで設定するようにしました。xl2tpd v1.2.4に対するパッチとして適宜適用して下さい。本来なら同じmsg_controlを使うIP_IPSEC_REFINFOと併用した際の挙動とかも確かめなきゃなんですけど、うちでは使ってないのでやってません;-)。パッチの適用についてはat your own riskでよろしくでございます。詳細は追って。

ファイル: xl2tpd-1.2.4.patch

diff -uNr ./xl2tpd-1.2.4.orig/call.c ./xl2tpd-1.2.4/call.c
--- ./xl2tpd-1.2.4.orig/call.c  2009-03-09 08:25:30.000000000 +0900
+++ ./xl2tpd-1.2.4/call.c       2010-05-14 03:34:40.461125516 +0900
@@ -618,7 +618,7 @@
 }
 
 struct call *get_call (int tunnel, int call, unsigned int addr, int port,
-                      IPsecSAref_t refme, IPsecSAref_t refhim)
+                      unsigned int localaddr, IPsecSAref_t refme, IPsecSAref_t refhim)
 {
     /*
      * Figure out which call struct should handle this. 
@@ -695,9 +695,11 @@
         };
         st->peer.sin_family = AF_INET;
         st->peer.sin_port = port;
+       st->local.sin_family = AF_INET;
        st->refme  = refme;
        st->refhim = refhim;
         bcopy (&addr, &st->peer.sin_addr, sizeof (addr));
+        bcopy (&localaddr, &st->local.sin_addr, sizeof (localaddr));
         st->next = tunnels.head;
         tunnels.head = st;
         tunnels.count++;
diff -uNr ./xl2tpd-1.2.4.orig/call.h ./xl2tpd-1.2.4/call.h
--- ./xl2tpd-1.2.4.orig/call.h  2009-03-09 08:25:30.000000000 +0900
+++ ./xl2tpd-1.2.4/call.h       2010-05-14 03:34:52.625126441 +0900
@@ -98,7 +98,7 @@
 extern void push_handler (int);
 extern void toss (struct buffer *);
 extern struct call *get_call (int tunnel, int call, unsigned int addr,
-                             int port,
+                             int port, unsigned int localaddr,
                              IPsecSAref_t refme, IPsecSAref_t refhim);
 extern struct call *get_tunnel (int, unsigned int, int);
 extern void destroy_call (struct call *);
diff -uNr ./xl2tpd-1.2.4.orig/l2tp.h ./xl2tpd-1.2.4/l2tp.h
--- ./xl2tpd-1.2.4.orig/l2tp.h  2009-03-09 08:25:30.000000000 +0900
+++ ./xl2tpd-1.2.4/l2tp.h       2010-05-14 03:38:04.172126651 +0900
@@ -146,6 +146,7 @@
     unsigned short port;        /* Port on remote end */
 #else
     struct sockaddr_in peer;    /* Peer's Address */
+    struct sockaddr_in local;   /* Local's Address */
 #endif
     int debug;                  /* Are we debugging or not? */
     int nego;                   /* Show Negotiation? */
diff -uNr ./xl2tpd-1.2.4.orig/network.c ./xl2tpd-1.2.4/network.c
--- ./xl2tpd-1.2.4.orig/network.c       2009-03-09 08:25:30.000000000 +0900
+++ ./xl2tpd-1.2.4/network.c    2010-05-14 04:03:15.057126011 +0900
@@ -77,6 +77,11 @@
 
            gconfig.ipsecsaref=0;
     }
+    arg=1;
+    if (setsockopt(server_socket, IPPROTO_IP, IP_PKTINFO, &arg, sizeof(arg)) != 0) {
+           l2tp_log(LOG_CRIT, "setsockopt recvref[%d]: %s\n", IP_PKTINFO, strerror(errno));
+    }
+
 #else
        l2tp_log(LOG_INFO, "No attempt being made to use IPsec SAref's since we're not on a Linux machine.\n");
 
@@ -257,8 +262,8 @@
 
 void udp_xmit (struct buffer *buf, struct tunnel *t)
 {
-    struct cmsghdr *cmsg;
-    char cbuf[CMSG_SPACE(sizeof (unsigned int))];
+    struct cmsghdr *cmsg = NULL;
+    char cbuf[CMSG_SPACE(sizeof (unsigned int)) + CMSG_SPACE(sizeof(struct in_pktinfo))];
     unsigned int *refp;
     struct msghdr msgh;
     int err;
@@ -288,6 +293,28 @@
 
        msgh.msg_controllen = cmsg->cmsg_len;
     }
+
+    if (t->local.sin_addr.s_addr) {
+        if(gconfig.debug_network) {
+                l2tp_log(LOG_DEBUG,"sending with setting source address saref=%s\n", inet_ntoa(t->local.sin_addr));
+        }
+
+        struct in_pktinfo* ipi;
+        msgh.msg_controllen = sizeof(cbuf);
+
+        cmsg = (cmsg ? CMSG_NXTHDR(&msgh, cmsg) : CMSG_FIRSTHDR(&msgh));
+        cmsg->cmsg_level = IPPROTO_IP;
+        cmsg->cmsg_type  = IP_PKTINFO;
+        cmsg->cmsg_len   = CMSG_LEN(sizeof(struct in_pktinfo));
+
+        ipi = (struct in_pktinfo*)CMSG_DATA(cmsg);
+        ipi->ipi_ifindex = 0;
+        ipi->ipi_addr.s_addr = 0;
+        bcopy(&t->local.sin_addr, &ipi->ipi_spec_dst, sizeof(ipi->ipi_spec_dst));
+
+        msgh.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+    }
+
     
     iov.iov_base = buf->start;
     iov.iov_len  = buf->len;
@@ -388,6 +415,8 @@
     struct iovec iov;
     char cbuf[256];
     unsigned int refme, refhim;
+    struct cmsghdr *cmsg;
+    struct in_pktinfo* inpkt = NULL;
 
     /* This one buffer can be recycled for everything except control packets */
     buf = new_buf (MAX_RECV_SIZE);
@@ -478,7 +507,6 @@
 
            /* extract IPsec info out */
            if(gconfig.ipsecsaref) {
-                   struct cmsghdr *cmsg;
                    /* Process auxiliary received data in msgh */
                    for (cmsg = CMSG_FIRSTHDR(&msgh);
                         cmsg != NULL;
@@ -494,6 +522,12 @@
                    }
            }
 
+            for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+                if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+                    inpkt = (struct in_pktinfo*)CMSG_DATA(cmsg); 
+                }
+            }
+
            /*
             * some logic could be added here to verify that we only
             * get L2TP packets inside of IPsec, or to provide different
@@ -517,7 +551,7 @@
            }
            if (!
                (c = get_call (tunnel, call, from.sin_addr.s_addr,
-                              from.sin_port, refme, refhim)))
+                              from.sin_port, (inpkt ? inpkt->ipi_spec_dst.s_addr : 0), refme, refhim)))
            {
                if ((c =
                     get_tunnel (tunnel, from.sin_addr.s_addr,
diff -uNr ./xl2tpd-1.2.4.orig/xl2tpd.c ./xl2tpd-1.2.4/xl2tpd.c
--- ./xl2tpd-1.2.4.orig/xl2tpd.c        2009-03-09 08:25:30.000000000 +0900
+++ ./xl2tpd-1.2.4/xl2tpd.c     2010-05-14 03:37:08.664125809 +0900
@@ -599,7 +599,7 @@
      * to do IPsec properly here, we need to set a socket policy,
      * and/or communicate with pluto.
      */
-    tmp = get_call (0, 0, addr, port, IPSEC_SAREF_NULL, IPSEC_SAREF_NULL);
+    tmp = get_call (0, 0, addr, port, 0, IPSEC_SAREF_NULL, IPSEC_SAREF_NULL);
     if (!tmp)
     {
         l2tp_log (LOG_WARNING, "%s: Unable to create tunnel to %s.\n", __FUNCTION__,

トラックバック(0)

トラックバックURL: http://foursics.jp/cgi-bin/mt/mt-tb.cgi/308

コメントする