adrienverge / openfortivpn

Client for PPP+TLS VPN tunnel services
GNU General Public License v3.0
2.75k stars 323 forks source link

Building openfortivpn on NetBSD #997

Open frantsao opened 2 years ago

frantsao commented 2 years ago

Hello, I'm trying to add openfortivpn to pkgsrc collection, to have openfortivpn available in several operating systems. I was hoping that it would be easy, but there is a problem when building on latest NetBSD:

===> Building for openfortivpn-1.17.3
  GEN      doc/openfortivpn.1
  GEN      etc/openfortivpn/config
  CC       src/openfortivpn-config.o
  CC       src/openfortivpn-http.o
  CC       src/openfortivpn-log.o
  CC       src/openfortivpn-hdlc.o
  CC       src/openfortivpn-tunnel.o
  CC       src/openfortivpn-io.o
  CC       src/openfortivpn-ipv4.o
  CC       src/openfortivpn-main.o
In file included from src/tunnel.h:34,
                 from src/http.h:21,
                 from src/http.c:18:
src/ipv4.h:36:8: error: redefinition of 'struct rtentry'
   36 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.h:24,
                 from src/tunnel.h:34,
                 from src/http.h:21,
                 from src/http.c:18:
/usr/include/net/route.h:109:8: note: originally defined here
  109 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.c:18:
src/ipv4.h:36:8: error: redefinition of 'struct rtentry'
   36 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.h:24,
                 from src/ipv4.c:18:
/usr/include/net/route.h:109:8: note: originally defined here
  109 | struct rtentry {
      |        ^~~~~~~
In file included from src/tunnel.h:34,
                 from src/main.c:19:
src/ipv4.h:36:8: error: redefinition of 'struct rtentry'
   36 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.h:24,
                 from src/tunnel.h:34,
                 from src/main.c:19:
/usr/include/net/route.h:109:8: note: originally defined here
  109 | struct rtentry {
      |        ^~~~~~~
In file included from src/tunnel.h:34,
                 from src/tunnel.c:29:
src/ipv4.h:36:8: error: redefinition of 'struct rtentry'
   36 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.h:24,
                 from src/tunnel.h:34,
                 from src/tunnel.c:29:
/usr/include/net/route.h:109:8: note: originally defined here
  109 | struct rtentry {
      |        ^~~~~~~
gmake: *** [Makefile:624: src/openfortivpn-tunnel.o] Error 1
gmake: *** Waiting for unfinished jobs....
In file included from /usr/include/ctype.h:100,
                 from src/config.c:35:
src/config.c: In function 'strtob':
src/config.c:130:22: warning: array subscript has type 'char' [-Wchar-subscripts]
  130 |  else if (isdigit(str[0]) == 0)
      |                      ^
src/config.c: In function 'load_config':
src/config.c:219:21: warning: array subscript has type 'char' [-Wchar-subscripts]
  219 |   while (isspace(key[0]))
      |                     ^
src/config.c:221:21: warning: array subscript has type 'char' [-Wchar-subscripts]
  221 |   while (isspace(val[0]))
      |                     ^
src/config.c:225:19: warning: array subscript has type 'char' [-Wchar-subscripts]
  225 |    if (isspace(key[i]))
      |                   ^
src/config.c:231:19: warning: array subscript has type 'char' [-Wchar-subscripts]
  231 |    if (isspace(val[i]))
      |                   ^
In file included from src/tunnel.h:34,
                 from src/io.c:32:
src/ipv4.h:36:8: error: redefinition of 'struct rtentry'
   36 | struct rtentry {
      |        ^~~~~~~
In file included from src/ipv4.h:24,
                 from src/tunnel.h:34,
                 from src/io.c:32:
/usr/include/net/route.h:109:8: note: originally defined here
  109 | struct rtentry {
      |        ^~~~~~~
In file included from /usr/include/ctype.h:100,
                 from src/http.c:29:
src/http.c: In function 'url_encode':
src/http.c:53:15: warning: array subscript has type 'char' [-Wchar-subscripts]
   53 |   if (isalnum(*str) || *str == '-' || *str == '_' ||
      |               ^
gmake: *** [Makefile:568: src/openfortivpn-http.o] Error 1
gmake: *** [Makefile:638: src/openfortivpn-main.o] Error 1
gmake: *** [Makefile:582: src/openfortivpn-io.o] Error 1
gmake: *** [Makefile:596: src/openfortivpn-ipv4.o] Error 1
*** Error code 2

I know that at this time there is not official support for NetBSD, but it would be wonderful for NetBSD users if it can be fixed :-)

DimitriPapadopoulos commented 2 years ago

The reason is pretty simple, that's because ipv4.h redefines struct rtentry on systems where it is not directly available in net/route.h (FreeBSD and macOS): https://github.com/adrienverge/openfortivpn/blob/8263c26822c977c2ed92f1ed6b8500067704227f/src/ipv4.h#L26-L36 To fix this, we need to find why HAVE_RT_ENTRY_WITH_RT_DST is not defined on NetBSD.

DimitriPapadopoulos commented 2 years ago

It appears struct rtentry, as defined in <net/route.h> on NetBSD does not contain rt_dst as required by openfortivpn:

struct rtentry {
    struct  radix_node rt_nodes[2]; /* tree glue, and other values */
#define rt_mask(r)  ((const struct sockaddr *)((r)->rt_nodes->rn_mask))
    struct  sockaddr *rt_gateway;   /* value */
    int rt_flags;       /* up/down?, host/net */
    int rt_refcnt;      /* # held references */
    uint64_t rt_use;            /* raw # packets forwarded */
    struct  ifnet *rt_ifp;      /* the answer: interface to use */
    struct  ifaddr *rt_ifa;     /* the answer: interface to use */
    uint32_t rt_ifa_seqno;
    void *  rt_llinfo;      /* pointer to link level info cache */
    struct  rt_metrics rt_rmx;  /* metrics used by rx'ing protocols */
    struct  rtentry *rt_gwroute;    /* implied entry for gatewayed routes */
    LIST_HEAD(, rttimer) rt_timer;  /* queue of timeouts for misc funcs */
    struct  rtentry *rt_parent; /* parent of cloned route */
    struct  sockaddr *_rt_key;
    struct  sockaddr *rt_tag;   /* route tagging info */
#ifdef _KERNEL
    kcondvar_t rt_cv;
    struct psref_target rt_psref;
    SLIST_ENTRY(rtentry) rt_free;   /* queue of deferred frees */
#endif
};
DimitriPapadopoulos commented 2 years ago

It looks like the openfortivpn source code supports these cases:

but not this case:

We need to adapt configure.ac: https://github.com/adrienverge/openfortivpn/blob/8263c26822c977c2ed92f1ed6b8500067704227f/configure.ac#L182-L210

But before we attempt that, please modify ipv4.h to not redefine struct rtentry. What happens then?

frantsao commented 2 years ago

I modified ipv4.h as you asked me, and now I get some different errors:

  CC       src/openfortivpn-ipv4.o
In file included from /usr/include/string.h:127,
                 from src/config.h:26,
                 from src/tunnel.h:32,
                 from src/ipv4.c:19:
src/ipv4.c: In function 'ipv4_show_route':
src/ipv4.h:54:49: error: 'struct rtentry' has no member named 'rt_dst'; did you mean 'rt_use'?
   54 | #define route_dest(route)  (cast_addr(&(route)->rt_dst)->sin_addr)
      |                                                 ^~~~~~
src/ipv4.c:73:39: note: in expansion of macro 'route_dest'
   73 |  strncat(show_route_buffer, inet_ntoa(route_dest(route)), 15);
      |                                       ^~~~~~~~~~
src/ipv4.h:54:49: error: 'struct rtentry' has no member named 'rt_dst'; did you mean 'rt_use'?
   54 | #define route_dest(route)  (cast_addr(&(route)->rt_dst)->sin_addr)
      |                                                 ^~~~~~
src/ipv4.c:73:39: note: in expansion of macro 'route_dest'
   73 |  strncat(show_route_buffer, inet_ntoa(route_dest(route)), 15);
      |                                       ^~~~~~~~~~
src/ipv4.h:55:47: error: 'struct rtentry' has no member named 'rt_genmask'
   55 | #define route_mask(route)  (cast_addr(&(route)->rt_genmask)->sin_addr)
      |                                               ^~
src/ipv4.c:75:39: note: in expansion of macro 'route_mask'
   75 |  strncat(show_route_buffer, inet_ntoa(route_mask(route)), 15);
      |                                       ^~~~~~~~~~
src/ipv4.h:55:47: error: 'struct rtentry' has no member named 'rt_genmask'
   55 | #define route_mask(route)  (cast_addr(&(route)->rt_genmask)->sin_addr)
      |                                               ^~
src/ipv4.c:75:39: note: in expansion of macro 'route_mask'
   75 |  strncat(show_route_buffer, inet_ntoa(route_mask(route)), 15);
      |                                       ^~~~~~~~~~
src/ipv4.h:56:39: warning: passing argument 1 of 'cast_addr' from incompatible pointer type [-Wincompatible-pointer-types]
   56 | #define route_gtw(route)   (cast_addr(&(route)->rt_gateway)->sin_addr)
      |                                       ^~~~~~~~~~~~~~~~~~~~
      |                                       |
      |                                       struct sockaddr **
src/ipv4.c:78:40: note: in expansion of macro 'route_gtw'
   78 |   strncat(show_route_buffer, inet_ntoa(route_gtw(route)), 15);
      |                                        ^~~~~~~~~
In file included from src/ipv4.c:18:
src/ipv4.h:50:62: note: expected 'struct sockaddr *' but argument is of type 'struct sockaddr **'
   50 | static inline struct sockaddr_in *cast_addr(struct sockaddr *addr)
      |                                             ~~~~~~~~~~~~~~~~~^~~~
[...]
DimitriPapadopoulos commented 2 years ago

Mmmh... So we do need to redefine struct rtentry, otherwise it does not contain rt_dst or rt_genmask. But then our re-definition struct rtentry conflicts with the one in <net/route.h>:

struct rtentry {
        struct  radix_node rt_nodes[2]; /* tree glue, and other values */
#define rt_mask(r)      ((const struct sockaddr *)((r)->rt_nodes->rn_mask))
        struct  sockaddr *rt_gateway;   /* value */
        int     rt_flags;               /* up/down?, host/net */
        int     rt_refcnt;              /* # held references */
        uint64_t rt_use;                        /* raw # packets forwarded */
        struct  ifnet *rt_ifp;          /* the answer: interface to use */
        struct  ifaddr *rt_ifa;         /* the answer: interface to use */
        uint32_t rt_ifa_seqno;
        void *  rt_llinfo;              /* pointer to link level info cache */
        struct  rt_metrics rt_rmx;      /* metrics used by rx'ing protocols */
        struct  rtentry *rt_gwroute;    /* implied entry for gatewayed routes */
        LIST_HEAD(, rttimer) rt_timer;  /* queue of timeouts for misc funcs */
        struct  rtentry *rt_parent;     /* parent of cloned route */
        struct  sockaddr *_rt_key;
        struct  sockaddr *rt_tag;       /* route tagging info */
};

Not sure how to work around that.

DimitriPapadopoulos commented 2 years ago

In the meantime, you could give OpenConnect a try. Does it work for you on NetBSD?

frantsao commented 2 years ago

Thanks, I gave a try and I was able to connect to a Fortinet VPN using OpenConnect 8.20, so there is an alternative :-) In any case, I hope that at some point you can find a solution, it would be great to add OpenFortiVPN to the pkgsrc collection.

DimitriPapadopoulos commented 2 years ago

OpenConnect 8.20 cannot connect to a Fortinet VPN. Either it was OpenConnect 9.01, or a checkout from the Git repository.

frantsao commented 2 years ago

Mmm maybe I have a mess in my testing env, but...

nbbuilds# openconnect --version
OpenConnect version v8.20
Using OpenSSL 1.1.1n  15 Mar 2022. Features present: TPM (OpenSSL ENGINE not present), HOTP software token, TOTP software token, DTLS, ESP
Supported protocols: anyconnect (default), nc, gp, pulse, f5, fortinet, array
Default vpnc-script (override with --script): /usr/pkg/etc/vpnc-script
DimitriPapadopoulos commented 2 years ago

My wrong, the short-lived OpenConnect 8.20 is indeed the first version to support Fortinet. Mixed it up with version 8.10.