| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 1 | This document describes how to use the kernel's L2TP drivers to | 
|  | 2 | provide L2TP functionality. L2TP is a protocol that tunnels one or | 
|  | 3 | more sessions over an IP tunnel. It is commonly used for VPNs | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 4 | (L2TP/IPSec) and by ISPs to tunnel subscriber PPP sessions over an IP | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 5 | network infrastructure. With L2TPv3, it is also useful as a Layer-2 | 
|  | 6 | tunneling infrastructure. | 
|  | 7 |  | 
|  | 8 | Features | 
|  | 9 | ======== | 
|  | 10 |  | 
|  | 11 | L2TPv2 (PPP over L2TP (UDP tunnels)). | 
|  | 12 | L2TPv3 ethernet pseudowires. | 
|  | 13 | L2TPv3 PPP pseudowires. | 
|  | 14 | L2TPv3 IP encapsulation. | 
|  | 15 | Netlink sockets for L2TPv3 configuration management. | 
|  | 16 |  | 
|  | 17 | History | 
|  | 18 | ======= | 
|  | 19 |  | 
|  | 20 | The original pppol2tp driver was introduced in 2.6.23 and provided | 
|  | 21 | L2TPv2 functionality (rfc2661). L2TPv2 is used to tunnel one or more PPP | 
|  | 22 | sessions over a UDP tunnel. | 
|  | 23 |  | 
|  | 24 | L2TPv3 (rfc3931) changes the protocol to allow different frame types | 
|  | 25 | to be passed over an L2TP tunnel by moving the PPP-specific parts of | 
|  | 26 | the protocol out of the core L2TP packet headers. Each frame type is | 
|  | 27 | known as a pseudowire type. Ethernet, PPP, HDLC, Frame Relay and ATM | 
|  | 28 | pseudowires for L2TP are defined in separate RFC standards. Another | 
|  | 29 | change for L2TPv3 is that it can be carried directly over IP with no | 
|  | 30 | UDP header (UDP is optional). It is also possible to create static | 
|  | 31 | unmanaged L2TPv3 tunnels manually without a control protocol | 
|  | 32 | (userspace daemon) to manage them. | 
|  | 33 |  | 
|  | 34 | To support L2TPv3, the original pppol2tp driver was split up to | 
|  | 35 | separate the L2TP and PPP functionality. Existing L2TPv2 userspace | 
|  | 36 | apps should be unaffected as the original pppol2tp sockets API is | 
|  | 37 | retained. L2TPv3, however, uses netlink to manage L2TPv3 tunnels and | 
|  | 38 | sessions. | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 39 |  | 
|  | 40 | Design | 
|  | 41 | ====== | 
|  | 42 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 43 | The L2TP protocol separates control and data frames.  The L2TP kernel | 
|  | 44 | drivers handle only L2TP data frames; control frames are always | 
|  | 45 | handled by userspace. L2TP control frames carry messages between L2TP | 
|  | 46 | clients/servers and are used to setup / teardown tunnels and | 
|  | 47 | sessions. An L2TP client or server is implemented in userspace. | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 48 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 49 | Each L2TP tunnel is implemented using a UDP or L2TPIP socket; L2TPIP | 
|  | 50 | provides L2TPv3 IP encapsulation (no UDP) and is implemented using a | 
|  | 51 | new l2tpip socket family. The tunnel socket is typically created by | 
|  | 52 | userspace, though for unmanaged L2TPv3 tunnels, the socket can also be | 
|  | 53 | created by the kernel. Each L2TP session (pseudowire) gets a network | 
|  | 54 | interface instance. In the case of PPP, these interfaces are created | 
|  | 55 | indirectly by pppd using a pppol2tp socket. In the case of ethernet, | 
|  | 56 | the netdevice is created upon a netlink request to create an L2TPv3 | 
|  | 57 | ethernet pseudowire. | 
|  | 58 |  | 
|  | 59 | For PPP, the PPPoL2TP driver, net/l2tp/l2tp_ppp.c, provides a | 
|  | 60 | mechanism by which PPP frames carried through an L2TP session are | 
|  | 61 | passed through the kernel's PPP subsystem. The standard PPP daemon, | 
|  | 62 | pppd, handles all PPP interaction with the peer. PPP network | 
|  | 63 | interfaces are created for each local PPP endpoint. The kernel's PPP | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 64 | subsystem arranges for PPP control frames to be delivered to pppd, | 
|  | 65 | while data frames are forwarded as usual. | 
|  | 66 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 67 | For ethernet, the L2TPETH driver, net/l2tp/l2tp_eth.c, implements a | 
|  | 68 | netdevice driver, managing virtual ethernet devices, one per | 
|  | 69 | pseudowire. These interfaces can be managed using standard Linux tools | 
|  | 70 | such as "ip" and "ifconfig". If only IP frames are passed over the | 
|  | 71 | tunnel, the interface can be given an IP addresses of itself and its | 
|  | 72 | peer. If non-IP frames are to be passed over the tunnel, the interface | 
|  | 73 | can be added to a bridge using brctl. All L2TP datapath protocol | 
|  | 74 | functions are handled by the L2TP core driver. | 
|  | 75 |  | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 76 | Each tunnel and session within a tunnel is assigned a unique tunnel_id | 
|  | 77 | and session_id. These ids are carried in the L2TP header of every | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 78 | control and data packet. (Actually, in L2TPv3, the tunnel_id isn't | 
|  | 79 | present in data frames - it is inferred from the IP connection on | 
|  | 80 | which the packet was received.) The L2TP driver uses the ids to lookup | 
|  | 81 | internal tunnel and/or session contexts to determine how to handle the | 
|  | 82 | packet. Zero tunnel / session ids are treated specially - zero ids are | 
|  | 83 | never assigned to tunnels or sessions in the network. In the driver, | 
|  | 84 | the tunnel context keeps a reference to the tunnel UDP or L2TPIP | 
|  | 85 | socket. The session context holds data that lets the driver interface | 
|  | 86 | to the kernel's network frame type subsystems, i.e. PPP, ethernet. | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 87 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 88 | Userspace Programming | 
|  | 89 | ===================== | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 90 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 91 | For L2TPv2, there are a number of requirements on the userspace L2TP | 
|  | 92 | daemon in order to use the pppol2tp driver. | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 93 |  | 
|  | 94 | 1. Use a UDP socket per tunnel. | 
|  | 95 |  | 
|  | 96 | 2. Create a single PPPoL2TP socket per tunnel bound to a special null | 
|  | 97 | session id. This is used only for communicating with the driver but | 
|  | 98 | must remain open while the tunnel is active. Opening this tunnel | 
|  | 99 | management socket causes the driver to mark the tunnel socket as an | 
|  | 100 | L2TP UDP encapsulation socket and flags it for use by the | 
|  | 101 | referenced tunnel id. This hooks up the UDP receive path via | 
|  | 102 | udp_encap_rcv() in net/ipv4/udp.c. PPP data frames are never passed | 
|  | 103 | in this special PPPoX socket. | 
|  | 104 |  | 
|  | 105 | 3. Create a PPPoL2TP socket per L2TP session. This is typically done | 
|  | 106 | by starting pppd with the pppol2tp plugin and appropriate | 
|  | 107 | arguments. A PPPoL2TP tunnel management socket (Step 2) must be | 
|  | 108 | created before the first PPPoL2TP session socket is created. | 
|  | 109 |  | 
|  | 110 | When creating PPPoL2TP sockets, the application provides information | 
|  | 111 | to the driver about the socket in a socket connect() call. Source and | 
|  | 112 | destination tunnel and session ids are provided, as well as the file | 
|  | 113 | descriptor of a UDP socket. See struct pppol2tp_addr in | 
| Paul Mackerras | 4b32da2 | 2012-03-04 12:56:55 +0000 | [diff] [blame] | 114 | include/linux/if_pppol2tp.h. Note that zero tunnel / session ids are | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 115 | treated specially. When creating the per-tunnel PPPoL2TP management | 
|  | 116 | socket in Step 2 above, zero source and destination session ids are | 
|  | 117 | specified, which tells the driver to prepare the supplied UDP file | 
|  | 118 | descriptor for use as an L2TP tunnel socket. | 
|  | 119 |  | 
|  | 120 | Userspace may control behavior of the tunnel or session using | 
|  | 121 | setsockopt and ioctl on the PPPoX socket. The following socket | 
|  | 122 | options are supported:- | 
|  | 123 |  | 
|  | 124 | DEBUG     - bitmask of debug message categories. See below. | 
|  | 125 | SENDSEQ   - 0 => don't send packets with sequence numbers | 
|  | 126 | 1 => send packets with sequence numbers | 
|  | 127 | RECVSEQ   - 0 => receive packet sequence numbers are optional | 
|  | 128 | 1 => drop receive packets without sequence numbers | 
|  | 129 | LNSMODE   - 0 => act as LAC. | 
|  | 130 | 1 => act as LNS. | 
|  | 131 | REORDERTO - reorder timeout (in millisecs). If 0, don't try to reorder. | 
|  | 132 |  | 
|  | 133 | Only the DEBUG option is supported by the special tunnel management | 
|  | 134 | PPPoX socket. | 
|  | 135 |  | 
|  | 136 | In addition to the standard PPP ioctls, a PPPIOCGL2TPSTATS is provided | 
|  | 137 | to retrieve tunnel and session statistics from the kernel using the | 
|  | 138 | PPPoX socket of the appropriate tunnel or session. | 
|  | 139 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 140 | For L2TPv3, userspace must use the netlink API defined in | 
|  | 141 | include/linux/l2tp.h to manage tunnel and session contexts. The | 
|  | 142 | general procedure to create a new L2TP tunnel with one session is:- | 
|  | 143 |  | 
|  | 144 | 1. Open a GENL socket using L2TP_GENL_NAME for configuring the kernel | 
|  | 145 | using netlink. | 
|  | 146 |  | 
|  | 147 | 2. Create a UDP or L2TPIP socket for the tunnel. | 
|  | 148 |  | 
|  | 149 | 3. Create a new L2TP tunnel using a L2TP_CMD_TUNNEL_CREATE | 
|  | 150 | request. Set attributes according to desired tunnel parameters, | 
|  | 151 | referencing the UDP or L2TPIP socket created in the previous step. | 
|  | 152 |  | 
|  | 153 | 4. Create a new L2TP session in the tunnel using a | 
|  | 154 | L2TP_CMD_SESSION_CREATE request. | 
|  | 155 |  | 
|  | 156 | The tunnel and all of its sessions are closed when the tunnel socket | 
|  | 157 | is closed. The netlink API may also be used to delete sessions and | 
|  | 158 | tunnels. Configuration and status info may be set or read using netlink. | 
|  | 159 |  | 
|  | 160 | The L2TP driver also supports static (unmanaged) L2TPv3 tunnels. These | 
|  | 161 | are where there is no L2TP control message exchange with the peer to | 
|  | 162 | setup the tunnel; the tunnel is configured manually at each end of the | 
|  | 163 | tunnel. There is no need for an L2TP userspace application in this | 
|  | 164 | case -- the tunnel socket is created by the kernel and configured | 
|  | 165 | using parameters sent in the L2TP_CMD_TUNNEL_CREATE netlink | 
|  | 166 | request. The "ip" utility of iproute2 has commands for managing static | 
|  | 167 | L2TPv3 tunnels; do "ip l2tp help" for more information. | 
|  | 168 |  | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 169 | Debugging | 
|  | 170 | ========= | 
|  | 171 |  | 
|  | 172 | The driver supports a flexible debug scheme where kernel trace | 
|  | 173 | messages may be optionally enabled per tunnel and per session. Care is | 
|  | 174 | needed when debugging a live system since the messages are not | 
|  | 175 | rate-limited and a busy system could be swamped. Userspace uses | 
|  | 176 | setsockopt on the PPPoX socket to set a debug mask. | 
|  | 177 |  | 
|  | 178 | The following debug mask bits are available: | 
|  | 179 |  | 
|  | 180 | PPPOL2TP_MSG_DEBUG    verbose debug (if compiled in) | 
|  | 181 | PPPOL2TP_MSG_CONTROL  userspace - kernel interface | 
|  | 182 | PPPOL2TP_MSG_SEQ      sequence numbers handling | 
|  | 183 | PPPOL2TP_MSG_DATA     data packets | 
|  | 184 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 185 | If enabled, files under a l2tp debugfs directory can be used to dump | 
|  | 186 | kernel state about L2TP tunnels and sessions. To access it, the | 
|  | 187 | debugfs filesystem must first be mounted. | 
|  | 188 |  | 
|  | 189 | # mount -t debugfs debugfs /debug | 
|  | 190 |  | 
|  | 191 | Files under the l2tp directory can then be accessed. | 
|  | 192 |  | 
|  | 193 | # cat /debug/l2tp/tunnels | 
|  | 194 |  | 
|  | 195 | The debugfs files should not be used by applications to obtain L2TP | 
|  | 196 | state information because the file format is subject to change. It is | 
|  | 197 | implemented to provide extra debug information to help diagnose | 
|  | 198 | problems.) Users should use the netlink API. | 
|  | 199 |  | 
|  | 200 | /proc/net/pppol2tp is also provided for backwards compaibility with | 
|  | 201 | the original pppol2tp driver. It lists information about L2TPv2 | 
|  | 202 | tunnels and sessions only. Its use is discouraged. | 
|  | 203 |  | 
|  | 204 | Unmanaged L2TPv3 Tunnels | 
|  | 205 | ======================== | 
|  | 206 |  | 
|  | 207 | Some commercial L2TP products support unmanaged L2TPv3 ethernet | 
|  | 208 | tunnels, where there is no L2TP control protocol; tunnels are | 
|  | 209 | configured at each side manually. New commands are available in | 
|  | 210 | iproute2's ip utility to support this. | 
|  | 211 |  | 
|  | 212 | To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1 | 
|  | 213 | and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the | 
|  | 214 | tunnel endpoints:- | 
|  | 215 |  | 
|  | 216 | # modprobe l2tp_eth | 
|  | 217 | # modprobe l2tp_netlink | 
|  | 218 |  | 
|  | 219 | # ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \ | 
|  | 220 | udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2 | 
|  | 221 | # ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1 | 
|  | 222 | # ifconfig -a | 
|  | 223 | # ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0 | 
|  | 224 | # ifconfig l2tpeth0 up | 
|  | 225 |  | 
|  | 226 | Choose IP addresses to be the address of a local IP interface and that | 
|  | 227 | of the remote system. The IP addresses of the l2tpeth0 interface can be | 
|  | 228 | anything suitable. | 
|  | 229 |  | 
|  | 230 | Repeat the above at the peer, with ports, tunnel/session ids and IP | 
|  | 231 | addresses reversed.  The tunnel and session IDs can be any non-zero | 
|  | 232 | 32-bit number, but the values must be reversed at the peer. | 
|  | 233 |  | 
|  | 234 | Host 1                         Host2 | 
|  | 235 | udp_sport=5000                 udp_sport=5001 | 
|  | 236 | udp_dport=5001                 udp_dport=5000 | 
|  | 237 | tunnel_id=42                   tunnel_id=45 | 
|  | 238 | peer_tunnel_id=45              peer_tunnel_id=42 | 
|  | 239 | session_id=128                 session_id=5196755 | 
|  | 240 | peer_session_id=5196755        peer_session_id=128 | 
|  | 241 |  | 
|  | 242 | When done at both ends of the tunnel, it should be possible to send | 
|  | 243 | data over the network. e.g. | 
|  | 244 |  | 
|  | 245 | # ping 10.5.1.1 | 
|  | 246 |  | 
|  | 247 |  | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 248 | Sample Userspace Code | 
|  | 249 | ===================== | 
|  | 250 |  | 
|  | 251 | 1. Create tunnel management PPPoX socket | 
|  | 252 |  | 
|  | 253 | kernel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); | 
|  | 254 | if (kernel_fd >= 0) { | 
|  | 255 | struct sockaddr_pppol2tp sax; | 
|  | 256 | struct sockaddr_in const *peer_addr; | 
|  | 257 |  | 
|  | 258 | peer_addr = l2tp_tunnel_get_peer_addr(tunnel); | 
|  | 259 | memset(&sax, 0, sizeof(sax)); | 
|  | 260 | sax.sa_family = AF_PPPOX; | 
|  | 261 | sax.sa_protocol = PX_PROTO_OL2TP; | 
|  | 262 | sax.pppol2tp.fd = udp_fd;       /* fd of tunnel UDP socket */ | 
|  | 263 | sax.pppol2tp.addr.sin_addr.s_addr = peer_addr->sin_addr.s_addr; | 
|  | 264 | sax.pppol2tp.addr.sin_port = peer_addr->sin_port; | 
|  | 265 | sax.pppol2tp.addr.sin_family = AF_INET; | 
|  | 266 | sax.pppol2tp.s_tunnel = tunnel_id; | 
|  | 267 | sax.pppol2tp.s_session = 0;     /* special case: mgmt socket */ | 
|  | 268 | sax.pppol2tp.d_tunnel = 0; | 
|  | 269 | sax.pppol2tp.d_session = 0;     /* special case: mgmt socket */ | 
|  | 270 |  | 
|  | 271 | if(connect(kernel_fd, (struct sockaddr *)&sax, sizeof(sax) ) < 0 ) { | 
|  | 272 | perror("connect failed"); | 
|  | 273 | result = -errno; | 
|  | 274 | goto err; | 
|  | 275 | } | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | 2. Create session PPPoX data socket | 
|  | 279 |  | 
|  | 280 | struct sockaddr_pppol2tp sax; | 
|  | 281 | int fd; | 
|  | 282 |  | 
|  | 283 | /* Note, the target socket must be bound already, else it will not be ready */ | 
|  | 284 | sax.sa_family = AF_PPPOX; | 
|  | 285 | sax.sa_protocol = PX_PROTO_OL2TP; | 
|  | 286 | sax.pppol2tp.fd = tunnel_fd; | 
|  | 287 | sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr; | 
|  | 288 | sax.pppol2tp.addr.sin_port = addr->sin_port; | 
|  | 289 | sax.pppol2tp.addr.sin_family = AF_INET; | 
|  | 290 | sax.pppol2tp.s_tunnel  = tunnel_id; | 
|  | 291 | sax.pppol2tp.s_session = session_id; | 
|  | 292 | sax.pppol2tp.d_tunnel  = peer_tunnel_id; | 
|  | 293 | sax.pppol2tp.d_session = peer_session_id; | 
|  | 294 |  | 
|  | 295 | /* session_fd is the fd of the session's PPPoL2TP socket. | 
|  | 296 | * tunnel_fd is the fd of the tunnel UDP socket. | 
|  | 297 | */ | 
|  | 298 | fd = connect(session_fd, (struct sockaddr *)&sax, sizeof(sax)); | 
|  | 299 | if (fd < 0 )    { | 
|  | 300 | return -errno; | 
|  | 301 | } | 
|  | 302 | return 0; | 
|  | 303 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 304 | Internal Implementation | 
|  | 305 | ======================= | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 306 |  | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 307 | The driver keeps a struct l2tp_tunnel context per L2TP tunnel and a | 
|  | 308 | struct l2tp_session context for each session. The l2tp_tunnel is | 
|  | 309 | always associated with a UDP or L2TP/IP socket and keeps a list of | 
|  | 310 | sessions in the tunnel. The l2tp_session context keeps kernel state | 
|  | 311 | about the session. It has private data which is used for data specific | 
|  | 312 | to the session type. With L2TPv2, the session always carried PPP | 
|  | 313 | traffic. With L2TPv3, the session can also carry ethernet frames | 
|  | 314 | (ethernet pseudowire) or other data types such as ATM, HDLC or Frame | 
|  | 315 | Relay. | 
|  | 316 |  | 
|  | 317 | When a tunnel is first opened, the reference count on the socket is | 
|  | 318 | increased using sock_hold(). This ensures that the kernel socket | 
|  | 319 | cannot be removed while L2TP's data structures reference it. | 
|  | 320 |  | 
|  | 321 | Some L2TP sessions also have a socket (PPP pseudowires) while others | 
|  | 322 | do not (ethernet pseudowires). We can't use the socket reference count | 
|  | 323 | as the reference count for session contexts. The L2TP implementation | 
|  | 324 | therefore has its own internal reference counts on the session | 
|  | 325 | contexts. | 
|  | 326 |  | 
|  | 327 | To Do | 
|  | 328 | ===== | 
|  | 329 |  | 
|  | 330 | Add L2TP tunnel switching support. This would route tunneled traffic | 
|  | 331 | from one L2TP tunnel into another. Specified in | 
|  | 332 | http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08 | 
|  | 333 |  | 
|  | 334 | Add L2TPv3 VLAN pseudowire support. | 
|  | 335 |  | 
|  | 336 | Add L2TPv3 IP pseudowire support. | 
|  | 337 |  | 
|  | 338 | Add L2TPv3 ATM pseudowire support. | 
|  | 339 |  | 
|  | 340 | Miscellaneous | 
|  | 341 | ============= | 
|  | 342 |  | 
|  | 343 | The L2TP drivers were developed as part of the OpenL2TP project by | 
| James Chapman | 58e50a9 | 2007-06-27 15:53:49 -0700 | [diff] [blame] | 344 | Katalix Systems Ltd. OpenL2TP is a full-featured L2TP client / server, | 
|  | 345 | designed from the ground up to have the L2TP datapath in the | 
|  | 346 | kernel. The project also implemented the pppol2tp plugin for pppd | 
|  | 347 | which allows pppd to use the kernel driver. Details can be found at | 
| James Chapman | 2f77a3f | 2010-04-02 06:19:46 +0000 | [diff] [blame] | 348 | http://www.openl2tp.org. |