Nathan Skrzypczak | 9ad39c0 | 2021-08-19 11:38:06 +0200 | [diff] [blame] | 1 | VPP as a Home Gateway |
| 2 | ===================== |
| 3 | |
| 4 | Vpp running on a small system (with appropriate NICs) makes a fine home |
| 5 | gateway. The resulting system performs far in excess of requirements: a |
| 6 | debug image runs at a vector size of ~1.2 terminating a 150-mbit down / |
| 7 | 10-mbit up cable modem connection. |
| 8 | |
| 9 | At a minimum, install sshd and the isc-dhcp-server. If you prefer, you |
| 10 | can use dnsmasq. |
| 11 | |
| 12 | System configuration files |
| 13 | -------------------------- |
| 14 | |
| 15 | /etc/vpp/startup.conf: |
| 16 | |
| 17 | .. code-block:: c |
| 18 | |
| 19 | unix { |
| 20 | nodaemon |
| 21 | log /var/log/vpp/vpp.log |
| 22 | full-coredump |
| 23 | cli-listen /run/vpp/cli.sock |
| 24 | startup-config /setup.gate |
| 25 | poll-sleep-usec 100 |
| 26 | gid vpp |
| 27 | } |
| 28 | api-segment { |
| 29 | gid vpp |
| 30 | } |
| 31 | dpdk { |
| 32 | dev 0000:03:00.0 |
| 33 | dev 0000:14:00.0 |
| 34 | etc. |
| 35 | } |
| 36 | |
| 37 | plugins { |
| 38 | ## Disable all plugins, selectively enable specific plugins |
| 39 | ## YMMV, you may wish to enable other plugins (acl, etc.) |
| 40 | plugin default { disable } |
| 41 | plugin dpdk_plugin.so { enable } |
| 42 | plugin nat_plugin.so { enable } |
| 43 | ## if you plan to use the time-based MAC filter |
| 44 | plugin mactime_plugin.so { enable } |
| 45 | } |
| 46 | |
| 47 | /etc/dhcp/dhcpd.conf: |
| 48 | |
| 49 | .. code-block:: c |
| 50 | |
| 51 | subnet 192.168.1.0 netmask 255.255.255.0 { |
| 52 | range 192.168.1.10 192.168.1.99; |
| 53 | option routers 192.168.1.1; |
| 54 | option domain-name-servers 8.8.8.8; |
| 55 | } |
| 56 | |
| 57 | If you decide to enable the vpp dns name resolver, substitute |
| 58 | 192.168.1.2 for 8.8.8.8 in the dhcp server configuration. |
| 59 | |
| 60 | /etc/default/isc-dhcp-server: |
| 61 | |
| 62 | .. code-block:: c |
| 63 | |
| 64 | # On which interfaces should the DHCP server (dhcpd) serve DHCP requests? |
| 65 | # Separate multiple interfaces with spaces, e.g. "eth0 eth1". |
| 66 | INTERFACESv4="lstack" |
| 67 | INTERFACESv6="" |
| 68 | |
| 69 | /etc/ssh/sshd_config: |
| 70 | |
| 71 | .. code-block:: c |
| 72 | |
| 73 | # What ports, IPs and protocols we listen for |
| 74 | Port <REDACTED-high-number-port> |
| 75 | # Change to no to disable tunnelled clear text passwords |
| 76 | PasswordAuthentication no |
| 77 | |
| 78 | For your own comfort and safety, do NOT allow password authentication |
| 79 | and do not answer ssh requests on port 22. Experience shows several hack |
| 80 | attempts per hour on port 22, but none (ever) on random high-number |
| 81 | ports. |
| 82 | |
| 83 | Systemd configuration |
| 84 | --------------------- |
| 85 | |
| 86 | In a typical home-gateway use-case, vpp owns the one-and-only WAN link |
| 87 | with a prayer of reaching the public internet. Simple things like |
| 88 | updating distro software requires use of the "lstack" interface created |
| 89 | above, and configuring a plausible upstream DNS name resolver. |
| 90 | |
| 91 | Configure /etc/systemd/resolved.conf as follows. |
| 92 | |
| 93 | /etc/systemd/resolved.conf: |
| 94 | |
| 95 | .. code-block:: c |
| 96 | |
| 97 | [Resolve] |
| 98 | DNS=8.8.8.8 |
| 99 | #FallbackDNS= |
| 100 | #Domains= |
| 101 | #LLMNR=no |
| 102 | #MulticastDNS=no |
| 103 | #DNSSEC=no |
| 104 | #Cache=yes |
| 105 | #DNSStubListener=yes |
| 106 | |
| 107 | Netplan configuration |
| 108 | --------------------- |
| 109 | |
| 110 | If you want to configure a static IP address on one of your home-gateway |
| 111 | Ethernet ports on Ubuntu 18.04, you'll need to configure netplan. |
| 112 | Netplan is relatively new. It and the network manager GUI and can be |
| 113 | cranky. In the configuration shown below, s/enp4s0/<your-interface>/... |
| 114 | |
| 115 | /etc/netplan-01-netcfg.yaml: |
| 116 | |
| 117 | .. code-block:: c |
| 118 | |
| 119 | # This file describes the network interfaces available on your system |
| 120 | # For more information, see netplan(5). |
| 121 | network: |
| 122 | version: 2 |
| 123 | renderer: networkd |
| 124 | ethernets: |
| 125 | enp4s0: |
| 126 | dhcp4: no |
| 127 | addresses: [192.168.2.254/24] |
| 128 | gateway4: 192.168.2.100 |
| 129 | nameservers: |
| 130 | search: [my.local] |
| 131 | addresses: [8.8.8.8] |
| 132 | |
| 133 | /etc/systemd/network-10.enp4s0.network: |
| 134 | |
| 135 | .. code-block:: c |
| 136 | |
| 137 | [Match] |
| 138 | Name=enp4s0 |
| 139 | |
| 140 | [Link] |
| 141 | RequiredForOnline=no |
| 142 | |
| 143 | [Network] |
| 144 | ConfigureWithoutCarrier=true |
| 145 | Address=192.168.2.254/24 |
| 146 | |
| 147 | Note that we've picked an IP address for the home gateway which is on an |
| 148 | independent unrouteable subnet. This is handy for installing (and |
| 149 | possibly reverting) new vpp software. |
| 150 | |
| 151 | VPP Configuration Files |
| 152 | ----------------------- |
| 153 | |
| 154 | Here we see a nice use-case for the vpp debug CLI macro expander: |
| 155 | |
| 156 | /setup.gate: |
| 157 | |
| 158 | .. code-block:: c |
| 159 | |
| 160 | define HOSTNAME vpp1 |
| 161 | define TRUNK GigabitEthernet3/0/0 |
| 162 | |
| 163 | comment { Specific MAC address yields a constant IP address } |
| 164 | define TRUNK_MACADDR 48:f8:b3:00:01:01 |
| 165 | define BVI_MACADDR 48:f8:b3:01:01:02 |
| 166 | |
| 167 | comment { inside subnet 192.168.<inside_subnet>.0/24 } |
| 168 | define INSIDE_SUBNET 1 |
| 169 | |
| 170 | define INSIDE_PORT1 GigabitEthernet6/0/0 |
| 171 | define INSIDE_PORT2 GigabitEthernet6/0/1 |
| 172 | define INSIDE_PORT3 GigabitEthernet8/0/0 |
| 173 | define INSIDE_PORT4 GigabitEthernet8/0/1 |
| 174 | |
| 175 | comment { feature selections } |
| 176 | define FEATURE_NAT44 comment |
| 177 | define FEATURE_CNAT uncomment |
| 178 | define FEATURE_DNS comment |
| 179 | define FEATURE_IP6 comment |
| 180 | define FEATURE_MACTIME uncomment |
| 181 | |
| 182 | exec /setup.tmpl |
| 183 | |
| 184 | /setup.tmpl: |
| 185 | |
| 186 | .. code-block:: c |
| 187 | |
| 188 | show macro |
| 189 | |
| 190 | set int mac address $(TRUNK) $(TRUNK_MACADDR) |
| 191 | set dhcp client intfc $(TRUNK) hostname $(HOSTNAME) |
| 192 | set int state $(TRUNK) up |
| 193 | |
| 194 | bvi create instance 0 |
| 195 | set int mac address bvi0 $(BVI_MACADDR) |
| 196 | set int l2 bridge bvi0 1 bvi |
| 197 | set int ip address bvi0 192.168.$(INSIDE_SUBNET).1/24 |
| 198 | set int state bvi0 up |
| 199 | |
| 200 | set int l2 bridge $(INSIDE_PORT1) 1 |
| 201 | set int state $(INSIDE_PORT1) up |
| 202 | set int l2 bridge $(INSIDE_PORT2) 1 |
| 203 | set int state $(INSIDE_PORT2) up |
| 204 | set int l2 bridge $(INSIDE_PORT3) 1 |
| 205 | set int state $(INSIDE_PORT3) up |
| 206 | set int l2 bridge $(INSIDE_PORT4) 1 |
| 207 | set int state $(INSIDE_PORT4) up |
| 208 | |
| 209 | comment { dhcp server and host-stack access } |
| 210 | create tap host-if-name lstack host-ip4-addr 192.168.$(INSIDE_SUBNET).2/24 host-ip4-gw 192.168.$(INSIDE_SUBNET).1 |
| 211 | set int l2 bridge tap0 1 |
| 212 | set int state tap0 up |
| 213 | |
| 214 | service restart isc-dhcp-server |
| 215 | |
| 216 | $(FEATURE_NAT44) { nat44 enable users 50 user-sessions 750 sessions 63000 } |
| 217 | $(FEATURE_NAT44) { nat44 add interface address $(TRUNK) } |
| 218 | $(FEATURE_NAT44) { set interface nat44 in bvi0 out $(TRUNK) } |
| 219 | |
| 220 | $(FEATURE_NAT44) { nat44 add static mapping local 192.168.$(INSIDE_SUBNET).2 22432 external $(TRUNK) 22432 tcp } |
| 221 | |
| 222 | $(FEATURE_CNAT) { cnat snat with $(TRUNK) } |
| 223 | $(FEATURE_CNAT) { set interface feature bvi0 ip4-cnat-snat arc ip4-unicast } |
| 224 | $(FEATURE_CNAT) { cnat translation add proto tcp real $(TRUNK) 22432 to -> 192.168.$(INSIDE_SUBNET).2 22432 } |
| 225 | $(FEATURE_CNAT) { $(FEATURE_DNS) { cnat translation add proto udp real $(TRUNK) 53053 to -> 192.168.$(INSIDE_SUBNET).1 53053 } } |
| 226 | |
| 227 | $(FEATURE_DNS) { $(FEATURE_NAT44) { nat44 add identity mapping external $(TRUNK) udp 53053 } } |
| 228 | $(FEATURE_DNS) { bin dns_name_server_add_del 8.8.8.8 } |
| 229 | $(FEATURE_DNS) { bin dns_enable_disable } |
| 230 | |
| 231 | comment { set ct6 inside $(TRUNK) } |
| 232 | comment { set ct6 outside $(TRUNK) } |
| 233 | |
| 234 | $(FEATURE_IP6) { set int ip6 table $(TRUNK) 0 } |
| 235 | $(FEATURE_IP6) { ip6 nd address autoconfig $(TRUNK) default-route } |
| 236 | $(FEATURE_IP6) { dhcp6 client $(TRUNK) } |
| 237 | $(FEATURE_IP6) { dhcp6 pd client $(TRUNK) prefix group hgw } |
| 238 | $(FEATURE_IP6) { set ip6 address bvi0 prefix group hgw ::1/64 } |
| 239 | $(FEATURE_IP6) { ip6 nd address autoconfig bvi0 default-route } |
| 240 | comment { iPhones seem to need lots of RA messages... } |
| 241 | $(FEATURE_IP6) { ip6 nd bvi0 ra-managed-config-flag ra-other-config-flag ra-interval 5 3 ra-lifetime 180 } |
| 242 | comment { ip6 nd bvi0 prefix 0::0/0 ra-lifetime 100000 } |
| 243 | |
| 244 | |
| 245 | $(FEATURE_MACTIME) { bin mactime_add_del_range name cisco-vpn mac a8:b4:56:e1:b8:3e allow-static } |
| 246 | $(FEATURE_MACTIME) { bin mactime_add_del_range name old-mac mac <redacted> allow-static } |
| 247 | $(FEATURE_MACTIME) { bin mactime_add_del_range name roku mac <redacted> allow-static } |
| 248 | $(FEATURE_MACTIME) { bin mactime_enable_disable $(INSIDE_PORT1) } |
| 249 | $(FEATURE_MACTIME) { bin mactime_enable_disable $(INSIDE_PORT2) } |
| 250 | $(FEATURE_MACTIME) { bin mactime_enable_disable $(INSIDE_PORT3) } |
| 251 | $(FEATURE_MACTIME) { bin mactime_enable_disable $(INSIDE_PORT4) } |
| 252 | |
| 253 | Installing new vpp software |
| 254 | --------------------------- |
| 255 | |
| 256 | If you're **sure** that a given set of vpp Debian packages will install |
| 257 | and work properly, you can install them while logged into the gateway |
| 258 | via the lstack / nat path. This procedure is a bit like standing on a |
| 259 | rug and yanking it. If all goes well, a perfect back-flip occurs. If |
| 260 | not, you may wish that you'd configured a static IP address on a |
| 261 | reserved Ethernet interface as described above. |
| 262 | |
| 263 | Installing a new vpp image via ssh to 192.168.1.2: |
| 264 | |
| 265 | .. code-block:: c |
| 266 | |
| 267 | # nohup dpkg -i *.deb >/dev/null 2>&1 & |
| 268 | |
| 269 | Within a few seconds, the inbound ssh connection SHOULD begin to respond |
| 270 | again. If it does not, you'll have to debug the issue(s). |
| 271 | |
| 272 | Reasonably Robust Remote Software Installation |
| 273 | ---------------------------------------------- |
| 274 | |
| 275 | Here are a couple of scripts which yield a reasonably robust software |
| 276 | installation scheme. |
| 277 | |
| 278 | Build-host script |
| 279 | ~~~~~~~~~~~~~~~~~ |
| 280 | |
| 281 | .. code-block:: c |
| 282 | |
| 283 | #!/bin/bash |
| 284 | |
| 285 | buildroot=/scratch/vpp-workspace/build-root |
| 286 | if [ $1x = "testx" ] ; then |
| 287 | subdir="test" |
| 288 | ipaddr="192.168.2.48" |
| 289 | elif [ $1x = "foox" ] ; then |
| 290 | subdir="foo" |
| 291 | ipaddr="foo.some.net" |
| 292 | elif [ $1x = "barx" ] ; then |
| 293 | subdir="bar" |
| 294 | ipaddr="bar.some.net" |
| 295 | else |
| 296 | subdir="test" |
| 297 | ipaddr="192.168.2.48" |
| 298 | fi |
| 299 | |
| 300 | echo Save current software... |
| 301 | ssh -p 22432 $ipaddr "rm -rf /gate_debians.prev" |
| 302 | ssh -p 22432 $ipaddr "mv /gate_debians /gate_debians.prev" |
| 303 | ssh -p 22432 $ipaddr "mkdir /gate_debians" |
| 304 | echo Copy new software to the gateway... |
| 305 | scp -P 22432 $buildroot/*.deb $ipaddr:/gate_debians |
| 306 | echo Install new software... |
| 307 | ssh -p 22432 $ipaddr "nohup /usr/local/bin/vpp-swupdate > /dev/null 2>&1 &" |
| 308 | |
| 309 | for i in 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 |
| 310 | do |
| 311 | echo Wait for $i seconds... |
| 312 | sleep 1 |
| 313 | done |
| 314 | |
| 315 | echo Try to access the device... |
| 316 | |
| 317 | ssh -p 22432 -o ConnectTimeout=10 $ipaddr "tail -20 /var/log/syslog | grep Ping" |
| 318 | if [ $? == 0 ] ; then |
| 319 | echo Access test OK... |
| 320 | else |
| 321 | echo Access failed, wait for configuration restoration... |
| 322 | for i in 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 |
| 323 | do |
| 324 | echo Wait for $i seconds... |
| 325 | sleep 1 |
| 326 | done |
| 327 | echo Retry access test |
| 328 | ssh -p 22432 -o ConnectTimeout=10 $ipaddr "tail -20 /var/log/syslog | grep Ping" |
| 329 | if [ $? == 0 ] ; then |
| 330 | echo Access test OK, check syslog on the device |
| 331 | exit 1 |
| 332 | else |
| 333 | echo Access test still fails, manual intervention required. |
| 334 | exit 2 |
| 335 | fi |
| 336 | fi |
| 337 | |
| 338 | exit 0 |
| 339 | |
| 340 | Target script |
| 341 | ~~~~~~~~~~~~~ |
| 342 | |
| 343 | .. code-block:: c |
| 344 | |
| 345 | #!/bin/bash |
| 346 | |
| 347 | logger "About to update vpp software..." |
| 348 | cd /gate_debians |
| 349 | service vpp stop |
| 350 | sudo dpkg -i *.deb >/dev/null 2>&1 & |
| 351 | sleep 20 |
| 352 | logger "Ping connectivity test..." |
| 353 | for i in 1 2 3 4 5 6 7 8 9 10 |
| 354 | do |
| 355 | ping -4 -c 1 yahoo.com |
| 356 | if [ $? == 0 ] ; then |
| 357 | logger "Ping test OK..." |
| 358 | exit 0 |
| 359 | fi |
| 360 | done |
| 361 | |
| 362 | logger "Ping test NOT OK, restore old software..." |
| 363 | rm -rf /gate_debians |
| 364 | mv /gate_debians.prev /gate_debians |
| 365 | cd /gate_debians |
| 366 | nohup sudo dpkg -i *.deb >/dev/null 2>&1 & |
| 367 | sleep 20 |
| 368 | logger "Repeat connectivity test..." |
| 369 | for i in 1 2 3 4 5 6 7 8 9 10 |
| 370 | do |
| 371 | ping -4 -c 1 yahoo.com |
| 372 | if [ $? == 0 ] ; then |
| 373 | logger "Ping test OK after restoring old software..." |
| 374 | exit 0 |
| 375 | fi |
| 376 | done |
| 377 | |
| 378 | logger "Ping test FAIL after restoring software, manual intervention required" |
| 379 | exit 2 |
| 380 | |
| 381 | Note that the target script **requires** that the user id which invokes |
| 382 | it will manage to “sudo dpkg …” without further authentication. If |
| 383 | you’re uncomfortable with the security implications of that requirement, |
| 384 | you’ll need to solve the problem a different way. Strongly suggest |
| 385 | configuring sshd as described above to minimize risk. |
| 386 | |
| 387 | Testing new software |
| 388 | -------------------- |
| 389 | |
| 390 | If you frequently test new home gateway software, it may be handy to set |
| 391 | up a test gateway behind your production gateway. This testing |
| 392 | methodology reduces complaints from family members, to name one benefit. |
| 393 | |
| 394 | Change the inside network (dhcp) subnet from 192.168.1.0/24 to |
| 395 | 192.168.3.0/24, change the (dhcp) advertised router to 192.168.3.1, |
| 396 | reconfigure the vpp tap interface addresses onto the 192.168.3.0/24 |
| 397 | subnet, and you should be all set. |
| 398 | |
| 399 | This scenario nats traffic twice: first, from the 192.168.3.0/24 network |
| 400 | onto the 192.168.1.0/24 network. Next, from the 192.168.1.0/24 network |
| 401 | onto the public internet. |
| 402 | |
| 403 | Patches |
| 404 | ------- |
| 405 | |
| 406 | You'll want this addition to src/vpp/vnet/main.c to add the "service |
| 407 | restart isc-dhcp-server” and "service restart vpp" commands: |
| 408 | |
| 409 | .. code-block:: c |
| 410 | |
| 411 | #include <sys/types.h> |
| 412 | #include <sys/wait.h> |
| 413 | |
| 414 | static int |
| 415 | mysystem (char *cmd) |
| 416 | { |
| 417 | int rv = 0; |
| 418 | |
| 419 | if (fork()) |
| 420 | wait (&rv); |
| 421 | else |
| 422 | execl("/bin/sh", "sh", "-c", cmd); |
| 423 | |
| 424 | if (rv != 0) |
| 425 | clib_unix_warning ("('%s') child process returned %d", cmd, rv); |
| 426 | return rv; |
| 427 | } |
| 428 | |
| 429 | static clib_error_t * |
| 430 | restart_isc_dhcp_server_command_fn (vlib_main_t * vm, |
| 431 | unformat_input_t * input, |
| 432 | vlib_cli_command_t * cmd) |
| 433 | { |
| 434 | int rv; |
| 435 | |
| 436 | /* Wait a while... */ |
| 437 | vlib_process_suspend (vm, 2.0); |
| 438 | |
| 439 | rv = mysystem("/usr/sbin/service isc-dhcp-server restart"); |
| 440 | |
| 441 | vlib_cli_output (vm, "Restarted the isc-dhcp-server, status %d...", rv); |
| 442 | return 0; |
| 443 | } |
| 444 | |
| 445 | VLIB_CLI_COMMAND (restart_isc_dhcp_server_command, static) = |
| 446 | { |
| 447 | .path = "service restart isc-dhcp-server", |
| 448 | .short_help = "restarts the isc-dhcp-server", |
| 449 | .function = restart_isc_dhcp_server_command_fn, |
| 450 | }; |
| 451 | |
| 452 | static clib_error_t * |
| 453 | restart_dora_tunnels_command_fn (vlib_main_t * vm, |
| 454 | unformat_input_t * input, |
| 455 | vlib_cli_command_t * cmd) |
| 456 | { |
| 457 | int rv; |
| 458 | |
| 459 | /* Wait three seconds... */ |
| 460 | vlib_process_suspend (vm, 3.0); |
| 461 | |
| 462 | rv = mysystem ("/usr/sbin/service dora restart"); |
| 463 | |
| 464 | vlib_cli_output (vm, "Restarted the dora tunnel service, status %d...", rv); |
| 465 | return 0; |
| 466 | } |
| 467 | |
| 468 | VLIB_CLI_COMMAND (restart_dora_tunnels_command, static) = |
| 469 | { |
| 470 | .path = "service restart dora", |
| 471 | .short_help = "restarts the dora tunnel service", |
| 472 | .function = restart_dora_tunnels_command_fn, |
| 473 | }; |
| 474 | |
| 475 | static clib_error_t * |
| 476 | restart_vpp_service_command_fn (vlib_main_t * vm, |
| 477 | unformat_input_t * input, |
| 478 | vlib_cli_command_t * cmd) |
| 479 | { |
| 480 | (void) mysystem ("/usr/sbin/service vpp restart"); |
| 481 | return 0; |
| 482 | } |
| 483 | |
| 484 | VLIB_CLI_COMMAND (restart_vpp_service_command, static) = |
| 485 | { |
| 486 | .path = "service restart vpp", |
| 487 | .short_help = "restarts the vpp service, be careful what you wish for", |
| 488 | .function = restart_vpp_service_command_fn, |
| 489 | }; |
| 490 | |
| 491 | Using the time-based mac filter plugin |
| 492 | -------------------------------------- |
| 493 | |
| 494 | If you need to restrict network access for certain devices to specific |
| 495 | daily time ranges, configure the "mactime" plugin. Add it to the list of |
| 496 | enabled plugins in /etc/vpp/startup.conf, then enable the feature on the |
| 497 | NAT "inside" interfaces: |
| 498 | |
| 499 | .. code-block:: c |
| 500 | |
| 501 | bin mactime_enable_disable GigabitEthernet0/14/0 |
| 502 | bin mactime_enable_disable GigabitEthernet0/14/1 |
| 503 | ... |
| 504 | |
| 505 | Create the required src-mac-address rule database. There are 4 rule |
| 506 | entry types: |
| 507 | |
| 508 | - allow-static - pass traffic from this mac address |
| 509 | - drop-static - drop traffic from this mac address |
| 510 | - allow-range - pass traffic from this mac address at specific times |
| 511 | - drop-range - drop traffic from this mac address at specific times |
| 512 | |
| 513 | Here are some examples: |
| 514 | |
| 515 | .. code-block:: c |
| 516 | |
| 517 | bin mactime_add_del_range name alarm-system mac 00:de:ad:be:ef:00 allow-static |
| 518 | bin mactime_add_del_range name unwelcome mac 00:de:ad:be:ef:01 drop-static |
| 519 | bin mactime_add_del_range name not-during-business-hours mac <mac> drop-range Mon - Fri 7:59 - 18:01 |
| 520 | bin mactime_add_del_range name monday-busines-hours mac <mac> allow-range Mon 7:59 - 18:01 |