1# Copyright 2020 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import re 16import time 17 18from acts import signals 19from acts import utils 20from acts.controllers.openwrt_lib import network_const 21 22 23SERVICE_DNSMASQ = "dnsmasq" 24SERVICE_STUNNEL = "stunnel" 25SERVICE_NETWORK = "network" 26SERVICE_PPTPD = "pptpd" 27SERVICE_FIREWALL = "firewall" 28SERVICE_IPSEC = "ipsec" 29SERVICE_XL2TPD = "xl2tpd" 30SERVICE_ODHCPD = "odhcpd" 31SERVICE_NODOGSPLASH = "nodogsplash" 32PPTP_PACKAGE = "pptpd kmod-nf-nathelper-extra" 33L2TP_PACKAGE = "strongswan-full openssl-util xl2tpd" 34NAT6_PACKAGE = "ip6tables kmod-ipt-nat6" 35CAPTIVE_PORTAL_PACKAGE = "nodogsplash" 36MDNS_PACKAGE = "avahi-utils avahi-daemon-service-http avahi-daemon-service-ssh libavahi-client avahi-dbus-daemon" 37STUNNEL_CONFIG_PATH = "/etc/stunnel/DoTServer.conf" 38HISTORY_CONFIG_PATH = "/etc/dirty_configs" 39PPTPD_OPTION_PATH = "/etc/ppp/options.pptpd" 40XL2TPD_CONFIG_PATH = "/etc/xl2tpd/xl2tpd.conf" 41XL2TPD_OPTION_CONFIG_PATH = "/etc/ppp/options.xl2tpd" 42FIREWALL_CUSTOM_OPTION_PATH = "/etc/firewall.user" 43PPP_CHAP_SECRET_PATH = "/etc/ppp/chap-secrets" 44TCPDUMP_DIR = "/tmp/tcpdump/" 45LOCALHOST = "192.168.1.1" 46DEFAULT_PACKAGE_INSTALL_TIMEOUT = 200 47 48 49class NetworkSettings(object): 50 """Class for network settings. 51 52 Attributes: 53 ssh: ssh connection object. 54 ssh_settings: ssh settings for AccessPoint. 55 service_manager: Object manage service configuration. 56 user: username for ssh. 57 ip: ip address for AccessPoint. 58 log: Logging object for AccessPoint. 59 config: A list to store changes on network settings. 60 firewall_rules_list: A list of firewall rule name list. 61 cleanup_map: A dict for compare oppo functions. 62 l2tp: profile for vpn l2tp server. 63 """ 64 65 def __init__(self, ssh, ssh_settings, logger): 66 """Initialize wireless settings. 67 68 Args: 69 ssh: ssh connection object. 70 ssh_settings: ssh settings for AccessPoint. 71 logger: Logging object for AccessPoint. 72 """ 73 self.ssh = ssh 74 self.service_manager = ServiceManager(ssh) 75 self.ssh_settings = ssh_settings 76 self.user = self.ssh_settings.username 77 self.ip = self.ssh_settings.hostname 78 self.log = logger 79 self.config = set() 80 self.firewall_rules_list = [] 81 self.cleanup_map = { 82 "setup_dns_server": self.remove_dns_server, 83 "setup_vpn_pptp_server": self.remove_vpn_pptp_server, 84 "setup_vpn_l2tp_server": self.remove_vpn_l2tp_server, 85 "disable_ipv6": self.enable_ipv6, 86 "setup_ipv6_bridge": self.remove_ipv6_bridge, 87 "default_dns": self.del_default_dns, 88 "default_v6_dns": self.del_default_v6_dns, 89 "ipv6_prefer_option": self.remove_ipv6_prefer_option, 90 "block_dns_response": self.unblock_dns_response, 91 "setup_mdns": self.remove_mdns, 92 "setup_captive_portal": self.remove_cpative_portal 93 } 94 # This map contains cleanup functions to restore the configuration to 95 # its default state. We write these keys to HISTORY_CONFIG_PATH prior to 96 # making any changes to that subsystem. 97 # This makes it easier to recover after an aborted test. 98 self.update_firewall_rules_list() 99 self.cleanup_network_settings() 100 self.clear_tcpdump() 101 102 def cleanup_network_settings(self): 103 """Reset all changes on Access point.""" 104 105 # Detect if any changes that is not clean up. 106 if self.file_exists(HISTORY_CONFIG_PATH): 107 out = self.ssh.run("cat %s" % HISTORY_CONFIG_PATH).stdout 108 if out: 109 self.config = set(out.split("\n")) 110 111 if self.config: 112 temp = self.config.copy() 113 for change in temp: 114 change_list = change.split() 115 if len(change_list) > 1: 116 self.cleanup_map[change_list[0]](*change_list[1:]) 117 else: 118 self.cleanup_map[change]() 119 self.config = set() 120 121 if self.file_exists(HISTORY_CONFIG_PATH): 122 out = self.ssh.run("cat %s" % HISTORY_CONFIG_PATH).stdout 123 if not out: 124 self.ssh.run("rm %s" % HISTORY_CONFIG_PATH) 125 126 def commit_changes(self): 127 """Apply changes on Access point.""" 128 self.ssh.run("uci commit") 129 self.service_manager.restart_services() 130 self.create_config_file("\n".join(self.config), 131 HISTORY_CONFIG_PATH) 132 133 def package_install(self, package_list): 134 """Install packages on OpenWrtAP via opkg If not installed. 135 136 Args: 137 package_list: package list to install. 138 e.g. "pptpd kmod-mppe kmod-nf-nathelper-extra" 139 """ 140 self.ssh.run("opkg update") 141 for package_name in package_list.split(" "): 142 if not self._package_installed(package_name): 143 self.ssh.run("opkg install %s" % package_name, 144 timeout=DEFAULT_PACKAGE_INSTALL_TIMEOUT) 145 self.log.info("Package: %s installed." % package_name) 146 else: 147 self.log.info("Package: %s skipped (already installed)." % package_name) 148 149 def package_remove(self, package_list): 150 """Remove packages on OpenWrtAP via opkg If existed. 151 152 Args: 153 package_list: package list to remove. 154 """ 155 for package_name in package_list.split(" "): 156 if self._package_installed(package_name): 157 self.ssh.run("opkg remove %s" % package_name) 158 self.log.info("Package: %s removed." % package_name) 159 else: 160 self.log.info("No exist package %s found." % package_name) 161 162 def _package_installed(self, package_name): 163 """Check if target package installed on OpenWrtAP. 164 165 Args: 166 package_name: package name want to check. 167 168 Returns: 169 True if installed. 170 """ 171 if self.ssh.run("opkg list-installed %s" % package_name).stdout: 172 return True 173 return False 174 175 def file_exists(self, abs_file_path): 176 """Check if target file exist on specific path on OpenWrt. 177 178 Args: 179 abs_file_path: Absolute path for the file. 180 181 Returns: 182 True if Existed. 183 """ 184 path, file_name = abs_file_path.rsplit("/", 1) 185 if self.ssh.run("ls %s | grep %s" % (path, file_name), 186 ignore_status=True).stdout: 187 return True 188 return False 189 190 def path_exists(self, abs_path): 191 """Check if dir exist on OpenWrt.""" 192 try: 193 self.ssh.run("ls %s" % abs_path) 194 except: 195 return False 196 return True 197 198 def count(self, config, key): 199 """Count in uci config. 200 201 Args: 202 config: config or section to research 203 key: keywords to e.g. rule, domain 204 Returns: 205 Numbers of the count. 206 """ 207 count = self.ssh.run("uci show %s | grep =%s" % (config, key), 208 ignore_status=True).stdout 209 return len(count.split("\n")) 210 211 def create_config_file(self, config, file_path): 212 """Create config file. Overwrite if file already exist. 213 214 Args: 215 config: A string of content of config. 216 file_path: Config's abs_path. 217 """ 218 self.ssh.run("echo -e \"%s\" > %s" % (config, file_path)) 219 220 def replace_config_option(self, old_option, new_option, file_path): 221 """Replace config option if pattern match. 222 223 If find match pattern with old_option, then replace it with new_option. 224 Else add new_option to the file. 225 226 Args: 227 old_option: the regexp pattern to replace. 228 new_option: the option to add. 229 file_path: Config's abs_path. 230 """ 231 config = self.ssh.run("cat %s" % file_path).stdout 232 config, count = re.subn(old_option, new_option, config) 233 if not count: 234 config = "\n".join([config, new_option]) 235 self.create_config_file(config, file_path) 236 237 def remove_config_option(self, option, file_path): 238 """Remove option from config file. 239 240 Args: 241 option: Option to remove. Support regular expression. 242 file_path: Config's abs_path. 243 Returns: 244 Boolean for find option to remove. 245 """ 246 config = self.ssh.run("cat %s" % file_path).stdout.split("\n") 247 for line in config: 248 count = re.subn(option, "", line)[1] 249 if count > 0: 250 config.remove(line) 251 self.create_config_file("\n".join(config), file_path) 252 return True 253 self.log.warning("No match option to remove.") 254 return False 255 256 def setup_dns_server(self, domain_name): 257 """Setup DNS server on OpenWrtAP. 258 259 Args: 260 domain_name: Local dns domain name. 261 """ 262 self.config.add("setup_dns_server") 263 self.log.info("Setup DNS server with domain name %s" % domain_name) 264 self.ssh.run("uci set dhcp.@dnsmasq[0].local='/%s/'" % domain_name) 265 self.ssh.run("uci set dhcp.@dnsmasq[0].domain='%s'" % domain_name) 266 self.add_resource_record(domain_name, self.ip) 267 self.service_manager.need_restart(SERVICE_DNSMASQ) 268 self.commit_changes() 269 270 # Check stunnel package is installed 271 self.package_install("stunnel") 272 self.service_manager.stop(SERVICE_STUNNEL) 273 self.service_manager.disable(SERVICE_STUNNEL) 274 275 # Enable stunnel 276 self.create_stunnel_config() 277 self.ssh.run("stunnel /etc/stunnel/DoTServer.conf") 278 279 def remove_dns_server(self): 280 """Remove DNS server on OpenWrtAP.""" 281 if self.file_exists("/var/run/stunnel.pid"): 282 self.ssh.run("kill $(cat /var/run/stunnel.pid)") 283 self.ssh.run("uci set dhcp.@dnsmasq[0].local='/lan/'") 284 self.ssh.run("uci set dhcp.@dnsmasq[0].domain='lan'") 285 self.clear_resource_record() 286 self.service_manager.need_restart(SERVICE_DNSMASQ) 287 self.config.discard("setup_dns_server") 288 self.commit_changes() 289 290 def add_resource_record(self, domain_name, domain_ip): 291 """Add resource record. 292 293 Args: 294 domain_name: A string for domain name. 295 domain_ip: A string for domain ip. 296 """ 297 self.ssh.run("uci add dhcp domain") 298 self.ssh.run("uci set dhcp.@domain[-1].name='%s'" % domain_name) 299 self.ssh.run("uci set dhcp.@domain[-1].ip='%s'" % domain_ip) 300 self.service_manager.need_restart(SERVICE_DNSMASQ) 301 302 def del_resource_record(self): 303 """Delete the last resource record.""" 304 self.ssh.run("uci delete dhcp.@domain[-1]") 305 self.service_manager.need_restart(SERVICE_DNSMASQ) 306 307 def clear_resource_record(self): 308 """Delete the all resource record.""" 309 rr = self.ssh.run("uci show dhcp | grep =domain", 310 ignore_status=True).stdout 311 if rr: 312 for _ in rr.split("\n"): 313 self.del_resource_record() 314 self.service_manager.need_restart(SERVICE_DNSMASQ) 315 316 def create_stunnel_config(self): 317 """Create config for stunnel service.""" 318 stunnel_config = [ 319 "pid = /var/run/stunnel.pid", 320 "[dns]", 321 "accept = 853", 322 "connect = 127.0.0.1:53", 323 "cert = /etc/stunnel/fullchain.pem", 324 "key = /etc/stunnel/privkey.pem", 325 ] 326 config_string = "\n".join(stunnel_config) 327 self.create_config_file(config_string, STUNNEL_CONFIG_PATH) 328 329 def setup_vpn_pptp_server(self, local_ip, user, password): 330 """Setup pptp vpn server on OpenWrt. 331 332 Args: 333 local_ip: local pptp server ip address. 334 user: username for pptp user. 335 password: password for pptp user. 336 """ 337 # Install pptp service 338 self.package_install(PPTP_PACKAGE) 339 340 self.config.add("setup_vpn_pptp_server") 341 # Edit /etc/config/pptpd & /etc/ppp/options.pptpd 342 self.setup_pptpd(local_ip, user, password) 343 # Edit /etc/config/firewall & /etc/firewall.user 344 self.setup_firewall_rules_for_pptp() 345 # Enable service 346 self.service_manager.enable(SERVICE_PPTPD) 347 self.service_manager.need_restart(SERVICE_PPTPD) 348 self.service_manager.need_restart(SERVICE_FIREWALL) 349 self.commit_changes() 350 351 def remove_vpn_pptp_server(self): 352 """Remove pptp vpn server on OpenWrt.""" 353 # Edit /etc/config/pptpd 354 self.restore_pptpd() 355 # Edit /etc/config/firewall & /etc/firewall.user 356 self.restore_firewall_rules_for_pptp() 357 # Disable service 358 self.service_manager.disable(SERVICE_PPTPD) 359 self.service_manager.need_restart(SERVICE_PPTPD) 360 self.service_manager.need_restart(SERVICE_FIREWALL) 361 self.config.discard("setup_vpn_pptp_server") 362 self.commit_changes() 363 364 self.package_remove(PPTP_PACKAGE) 365 self.ssh.run("rm /etc/ppp/options.pptpd") 366 self.ssh.run("rm /etc/config/pptpd") 367 368 def setup_pptpd(self, local_ip, username, password, ms_dns="8.8.8.8"): 369 """Setup pptpd config for ip addr and account. 370 371 Args: 372 local_ip: vpn server address 373 username: pptp vpn username 374 password: pptp vpn password 375 ms_dns: DNS server 376 """ 377 # Calculate remote ip address 378 # e.g. local_ip = 10.10.10.9 379 # remote_ip = 10.10.10.10 -250 380 remote_ip = local_ip.split(".") 381 remote_ip.append(str(int(remote_ip.pop(-1)) + 1)) 382 remote_ip = ".".join(remote_ip) 383 # Enable pptp service and set ip addr 384 self.ssh.run("uci set pptpd.pptpd.enabled=1") 385 self.ssh.run("uci set pptpd.pptpd.localip='%s'" % local_ip) 386 self.ssh.run("uci set pptpd.pptpd.remoteip='%s-250'" % remote_ip) 387 388 # Setup pptp service account 389 self.ssh.run("uci set pptpd.@login[0].username='%s'" % username) 390 self.ssh.run("uci set pptpd.@login[0].password='%s'" % password) 391 self.service_manager.need_restart(SERVICE_PPTPD) 392 393 self.replace_config_option(r"#*ms-dns \d+.\d+.\d+.\d+", 394 "ms-dns %s" % ms_dns, PPTPD_OPTION_PATH) 395 self.replace_config_option("(#no)*proxyarp", 396 "proxyarp", PPTPD_OPTION_PATH) 397 398 def restore_pptpd(self): 399 """Disable pptpd.""" 400 self.ssh.run("uci set pptpd.pptpd.enabled=0") 401 self.remove_config_option(r"\S+ pptp-server \S+ \*", 402 PPP_CHAP_SECRET_PATH) 403 self.service_manager.need_restart(SERVICE_PPTPD) 404 405 def setup_vpn_l2tp_server(self, 406 vpn_server_hostname, 407 vpn_server_address, 408 vpn_username, 409 vpn_password, 410 psk_secret, 411 server_name, 412 country, 413 org): 414 """Setup l2tp vpn server on OpenWrt. 415 416 Args: 417 vpn_server_hostname: vpn server domain name 418 vpn_server_address: vpn server addr 419 vpn_username: vpn account 420 vpn_password: vpn password 421 psk_secret: psk for ipsec 422 server_name: vpn server name for register in OpenWrt 423 country: country code for generate cert keys. 424 org: Organization name for generate cert keys. 425 """ 426 self.l2tp = network_const.VpnL2tp(vpn_server_hostname, 427 vpn_server_address, 428 vpn_username, 429 vpn_password, 430 psk_secret, 431 server_name) 432 433 self.package_install(L2TP_PACKAGE) 434 self.config.add("setup_vpn_l2tp_server") 435 436 # /etc/strongswan.conf: Strongswan configuration file 437 self.setup_strongswan() 438 # /etc/ipsec.conf /etc/ipsec.secrets 439 self.setup_ipsec() 440 # /etc/xl2tpd/xl2tpd.conf & /etc/ppp/options.xl2tpd 441 self.setup_xl2tpd() 442 # /etc/ppp/chap-secrets 443 self.setup_ppp_secret() 444 # /etc/config/firewall & /etc/firewall.user 445 self.setup_firewall_rules_for_l2tp() 446 # setup vpn server local ip 447 self.setup_vpn_local_ip() 448 # generate cert and key for rsa 449 self.generate_vpn_cert_keys(country, org) 450 # restart service 451 self.service_manager.need_restart(SERVICE_IPSEC) 452 self.service_manager.need_restart(SERVICE_XL2TPD) 453 self.service_manager.need_restart(SERVICE_FIREWALL) 454 self.commit_changes() 455 456 def remove_vpn_l2tp_server(self): 457 """Remove l2tp vpn server on OpenWrt.""" 458 self.config.discard("setup_vpn_l2tp_server") 459 self.restore_firewall_rules_for_l2tp() 460 self.remove_vpn_local_ip() 461 self.service_manager.need_restart(SERVICE_IPSEC) 462 self.service_manager.need_restart(SERVICE_XL2TPD) 463 self.service_manager.need_restart(SERVICE_FIREWALL) 464 self.commit_changes() 465 self.package_remove(L2TP_PACKAGE) 466 if hasattr(self, "l2tp"): 467 delattr(self, "l2tp") 468 469 def setup_strongswan(self, dns="8.8.8.8"): 470 """Setup strongswan config.""" 471 config = [ 472 "charon {", 473 " load_modular = yes", 474 " plugins {", 475 " include strongswan.d/charon/*.conf", 476 " }", 477 " dns1=%s" % dns, 478 "}" 479 ] 480 self.create_config_file("\n".join(config), "/etc/strongswan.conf") 481 482 def setup_ipsec(self): 483 """Setup ipsec config.""" 484 def load_ipsec_config(data, rightsourceip=False): 485 for i in data.keys(): 486 config.append(i) 487 for j in data[i].keys(): 488 config.append("\t %s=%s" % (j, data[i][j])) 489 if rightsourceip: 490 config.append("\t rightsourceip=%s.16/26" % self.l2tp.address.rsplit(".", 1)[0]) 491 config.append("") 492 493 config = [] 494 load_ipsec_config(network_const.IPSEC_CONF) 495 load_ipsec_config(network_const.IPSEC_L2TP_PSK) 496 load_ipsec_config(network_const.IPSEC_L2TP_RSA) 497 load_ipsec_config(network_const.IPSEC_HYBRID_RSA, True) 498 load_ipsec_config(network_const.IPSEC_XAUTH_PSK, True) 499 load_ipsec_config(network_const.IPSEC_XAUTH_RSA, True) 500 self.create_config_file("\n".join(config), "/etc/ipsec.conf") 501 502 ipsec_secret = [] 503 ipsec_secret.append(r": PSK \"%s\"" % self.l2tp.psk_secret) 504 ipsec_secret.append(r": RSA \"%s\"" % "serverKey.der") 505 ipsec_secret.append(r"%s : XAUTH \"%s\"" % (self.l2tp.username, 506 self.l2tp.password)) 507 self.create_config_file("\n".join(ipsec_secret), "/etc/ipsec.secrets") 508 509 def setup_xl2tpd(self, ip_range=20): 510 """Setup xl2tpd config.""" 511 net_id, host_id = self.l2tp.address.rsplit(".", 1) 512 xl2tpd_conf = list(network_const.XL2TPD_CONF_GLOBAL) 513 xl2tpd_conf.append("auth file = %s" % PPP_CHAP_SECRET_PATH) 514 xl2tpd_conf.extend(network_const.XL2TPD_CONF_INS) 515 xl2tpd_conf.append("ip range = %s.%s-%s.%s" % 516 (net_id, host_id, net_id, 517 str(int(host_id)+ip_range))) 518 xl2tpd_conf.append("local ip = %s" % self.l2tp.address) 519 xl2tpd_conf.append("name = %s" % self.l2tp.name) 520 xl2tpd_conf.append("pppoptfile = %s" % XL2TPD_OPTION_CONFIG_PATH) 521 522 self.create_config_file("\n".join(xl2tpd_conf), XL2TPD_CONFIG_PATH) 523 xl2tpd_option = list(network_const.XL2TPD_OPTION) 524 xl2tpd_option.append("name %s" % self.l2tp.name) 525 self.create_config_file("\n".join(xl2tpd_option), 526 XL2TPD_OPTION_CONFIG_PATH) 527 528 def setup_ppp_secret(self): 529 self.replace_config_option( 530 r"\S+ %s \S+ \*" % self.l2tp.name, 531 "%s %s %s *" % (self.l2tp.username, 532 self.l2tp.name, 533 self.l2tp.password), 534 PPP_CHAP_SECRET_PATH) 535 536 def generate_vpn_cert_keys(self, country, org): 537 """Generate cert and keys for vpn server.""" 538 rsa = "--type rsa" 539 lifetime = "--lifetime 365" 540 size = "--size 4096" 541 542 self.ssh.run("ipsec pki --gen %s %s --outform der > caKey.der" % 543 (rsa, size)) 544 self.ssh.run("ipsec pki --self --ca %s --in caKey.der %s --dn " 545 "\"C=%s, O=%s, CN=%s\" --outform der > caCert.der" % 546 (lifetime, rsa, country, org, self.l2tp.hostname)) 547 self.ssh.run("ipsec pki --gen %s %s --outform der > serverKey.der" % 548 (size, rsa)) 549 self.ssh.run("ipsec pki --pub --in serverKey.der %s | ipsec pki " 550 "--issue %s --cacert caCert.der --cakey caKey.der " 551 "--dn \"C=%s, O=%s, CN=%s\" --san %s --flag serverAuth" 552 " --flag ikeIntermediate --outform der > serverCert.der" % 553 (rsa, lifetime, country, org, self.l2tp.hostname, LOCALHOST)) 554 self.ssh.run("ipsec pki --gen %s %s --outform der > clientKey.der" % 555 (size, rsa)) 556 self.ssh.run("ipsec pki --pub --in clientKey.der %s | ipsec pki " 557 "--issue %s --cacert caCert.der --cakey caKey.der " 558 "--dn \"C=%s, O=%s, CN=%s@%s\" --outform der > " 559 "clientCert.der" % (rsa, lifetime, country, org, 560 self.l2tp.username, self.l2tp.hostname)) 561 562 self.ssh.run( 563 "openssl rsa -inform DER -in clientKey.der" 564 " -out clientKey.pem -outform PEM" 565 ) 566 self.ssh.run( 567 "openssl x509 -inform DER -in clientCert.der" 568 " -out clientCert.pem -outform PEM" 569 ) 570 self.ssh.run( 571 "openssl x509 -inform DER -in caCert.der" 572 " -out caCert.pem -outform PEM" 573 ) 574 self.ssh.run( 575 "openssl pkcs12 -in clientCert.pem -inkey clientKey.pem" 576 " -certfile caCert.pem -export -out clientPkcs.p12 -passout pass:" 577 ) 578 579 self.ssh.run("mv caCert.pem /etc/ipsec.d/cacerts/") 580 self.ssh.run("mv *Cert* /etc/ipsec.d/certs/") 581 self.ssh.run("mv *Key* /etc/ipsec.d/private/") 582 if not self.path_exists("/www/downloads/"): 583 self.ssh.run("mkdir /www/downloads/") 584 self.ssh.run("mv clientPkcs.p12 /www/downloads/") 585 self.ssh.run("chmod 664 /www/downloads/clientPkcs.p12") 586 587 def update_firewall_rules_list(self): 588 """Update rule list in /etc/config/firewall.""" 589 new_rules_list = [] 590 for i in range(self.count("firewall", "rule")): 591 rule = self.ssh.run("uci get firewall.@rule[%s].name" % i).stdout 592 new_rules_list.append(rule) 593 self.firewall_rules_list = new_rules_list 594 595 def setup_firewall_rules_for_pptp(self): 596 """Setup firewall for vpn pptp server.""" 597 self.update_firewall_rules_list() 598 if "pptpd" not in self.firewall_rules_list: 599 self.ssh.run("uci add firewall rule") 600 self.ssh.run("uci set firewall.@rule[-1].name='pptpd'") 601 self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'") 602 self.ssh.run("uci set firewall.@rule[-1].proto='tcp'") 603 self.ssh.run("uci set firewall.@rule[-1].dest_port='1723'") 604 self.ssh.run("uci set firewall.@rule[-1].family='ipv4'") 605 self.ssh.run("uci set firewall.@rule[-1].src='wan'") 606 607 if "GRP" not in self.firewall_rules_list: 608 self.ssh.run("uci add firewall rule") 609 self.ssh.run("uci set firewall.@rule[-1].name='GRP'") 610 self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'") 611 self.ssh.run("uci set firewall.@rule[-1].src='wan'") 612 self.ssh.run("uci set firewall.@rule[-1].proto='47'") 613 614 iptable_rules = list(network_const.FIREWALL_RULES_FOR_PPTP) 615 self.add_custom_firewall_rules(iptable_rules) 616 self.service_manager.need_restart(SERVICE_FIREWALL) 617 618 def restore_firewall_rules_for_pptp(self): 619 """Restore firewall for vpn pptp server.""" 620 self.update_firewall_rules_list() 621 if "pptpd" in self.firewall_rules_list: 622 self.ssh.run("uci del firewall.@rule[%s]" 623 % self.firewall_rules_list.index("pptpd")) 624 self.update_firewall_rules_list() 625 if "GRP" in self.firewall_rules_list: 626 self.ssh.run("uci del firewall.@rule[%s]" 627 % self.firewall_rules_list.index("GRP")) 628 self.remove_custom_firewall_rules() 629 self.service_manager.need_restart(SERVICE_FIREWALL) 630 631 def setup_firewall_rules_for_l2tp(self): 632 """Setup firewall for vpn l2tp server.""" 633 self.update_firewall_rules_list() 634 if "ipsec esp" not in self.firewall_rules_list: 635 self.ssh.run("uci add firewall rule") 636 self.ssh.run("uci set firewall.@rule[-1].name='ipsec esp'") 637 self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'") 638 self.ssh.run("uci set firewall.@rule[-1].proto='esp'") 639 self.ssh.run("uci set firewall.@rule[-1].src='wan'") 640 641 if "ipsec nat-t" not in self.firewall_rules_list: 642 self.ssh.run("uci add firewall rule") 643 self.ssh.run("uci set firewall.@rule[-1].name='ipsec nat-t'") 644 self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'") 645 self.ssh.run("uci set firewall.@rule[-1].src='wan'") 646 self.ssh.run("uci set firewall.@rule[-1].proto='udp'") 647 self.ssh.run("uci set firewall.@rule[-1].dest_port='4500'") 648 649 if "auth header" not in self.firewall_rules_list: 650 self.ssh.run("uci add firewall rule") 651 self.ssh.run("uci set firewall.@rule[-1].name='auth header'") 652 self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'") 653 self.ssh.run("uci set firewall.@rule[-1].src='wan'") 654 self.ssh.run("uci set firewall.@rule[-1].proto='ah'") 655 656 net_id = self.l2tp.address.rsplit(".", 1)[0] 657 iptable_rules = list(network_const.FIREWALL_RULES_FOR_L2TP) 658 iptable_rules.append("iptables -A FORWARD -s %s.0/24" 659 " -j ACCEPT" % net_id) 660 iptable_rules.append("iptables -t nat -A POSTROUTING" 661 " -s %s.0/24 -o eth0.2 -j MASQUERADE" % net_id) 662 663 self.add_custom_firewall_rules(iptable_rules) 664 self.service_manager.need_restart(SERVICE_FIREWALL) 665 666 def restore_firewall_rules_for_l2tp(self): 667 """Restore firewall for vpn l2tp server.""" 668 self.update_firewall_rules_list() 669 if "ipsec esp" in self.firewall_rules_list: 670 self.ssh.run("uci del firewall.@rule[%s]" 671 % self.firewall_rules_list.index("ipsec esp")) 672 self.update_firewall_rules_list() 673 if "ipsec nat-t" in self.firewall_rules_list: 674 self.ssh.run("uci del firewall.@rule[%s]" 675 % self.firewall_rules_list.index("ipsec nat-t")) 676 self.update_firewall_rules_list() 677 if "auth header" in self.firewall_rules_list: 678 self.ssh.run("uci del firewall.@rule[%s]" 679 % self.firewall_rules_list.index("auth header")) 680 self.remove_custom_firewall_rules() 681 self.service_manager.need_restart(SERVICE_FIREWALL) 682 683 def add_custom_firewall_rules(self, rules): 684 """Backup current custom rules and replace with arguments. 685 686 Args: 687 rules: A list of iptable rules to apply. 688 """ 689 backup_file_path = FIREWALL_CUSTOM_OPTION_PATH+".backup" 690 if not self.file_exists(backup_file_path): 691 self.ssh.run("mv %s %s" % (FIREWALL_CUSTOM_OPTION_PATH, 692 backup_file_path)) 693 for rule in rules: 694 self.ssh.run("echo %s >> %s" % (rule, FIREWALL_CUSTOM_OPTION_PATH)) 695 696 def remove_custom_firewall_rules(self): 697 """Clean up and recover custom firewall rules.""" 698 backup_file_path = FIREWALL_CUSTOM_OPTION_PATH+".backup" 699 if self.file_exists(backup_file_path): 700 self.ssh.run("mv %s %s" % (backup_file_path, 701 FIREWALL_CUSTOM_OPTION_PATH)) 702 else: 703 self.log.debug("Did not find %s" % backup_file_path) 704 self.ssh.run("echo "" > %s" % FIREWALL_CUSTOM_OPTION_PATH) 705 706 def disable_pptp_service(self): 707 """Disable pptp service.""" 708 self.package_remove(PPTP_PACKAGE) 709 710 def setup_vpn_local_ip(self): 711 """Setup VPN Server local ip on OpenWrt for client ping verify.""" 712 self.ssh.run("uci set network.lan2=interface") 713 self.ssh.run("uci set network.lan2.type=bridge") 714 self.ssh.run("uci set network.lan2.ifname=eth1.2") 715 self.ssh.run("uci set network.lan2.proto=static") 716 self.ssh.run("uci set network.lan2.ipaddr=\"%s\"" % self.l2tp.address) 717 self.ssh.run("uci set network.lan2.netmask=255.255.255.0") 718 self.ssh.run("uci set network.lan2=interface") 719 self.service_manager.reload(SERVICE_NETWORK) 720 self.commit_changes() 721 722 def remove_vpn_local_ip(self): 723 """Discard vpn local ip on OpenWrt.""" 724 self.ssh.run("uci delete network.lan2") 725 self.service_manager.reload(SERVICE_NETWORK) 726 self.commit_changes() 727 728 def enable_ipv6(self): 729 """Enable ipv6 on OpenWrt.""" 730 self.ssh.run("uci set network.lan.ipv6=1") 731 self.ssh.run("uci set network.wan.ipv6=1") 732 self.service_manager.enable("odhcpd") 733 self.service_manager.reload(SERVICE_NETWORK) 734 self.config.discard("disable_ipv6") 735 self.commit_changes() 736 737 def disable_ipv6(self): 738 """Disable ipv6 on OpenWrt.""" 739 self.config.add("disable_ipv6") 740 self.ssh.run("uci set network.lan.ipv6=0") 741 self.ssh.run("uci set network.wan.ipv6=0") 742 self.service_manager.disable("odhcpd") 743 self.service_manager.reload(SERVICE_NETWORK) 744 self.commit_changes() 745 746 def setup_ipv6_bridge(self): 747 """Setup ipv6 bridge for client have ability to access network.""" 748 self.config.add("setup_ipv6_bridge") 749 750 self.ssh.run("uci set dhcp.lan.dhcpv6=relay") 751 self.ssh.run("uci set dhcp.lan.ra=relay") 752 self.ssh.run("uci set dhcp.lan.ndp=relay") 753 754 self.ssh.run("uci set dhcp.wan6=dhcp") 755 self.ssh.run("uci set dhcp.wan6.dhcpv6=relay") 756 self.ssh.run("uci set dhcp.wan6.ra=relay") 757 self.ssh.run("uci set dhcp.wan6.ndp=relay") 758 self.ssh.run("uci set dhcp.wan6.master=1") 759 self.ssh.run("uci set dhcp.wan6.interface=wan6") 760 761 # Enable service 762 self.service_manager.need_restart(SERVICE_ODHCPD) 763 self.commit_changes() 764 765 def remove_ipv6_bridge(self): 766 """Discard ipv6 bridge on OpenWrt.""" 767 if "setup_ipv6_bridge" in self.config: 768 self.config.discard("setup_ipv6_bridge") 769 770 self.ssh.run("uci set dhcp.lan.dhcpv6=server") 771 self.ssh.run("uci set dhcp.lan.ra=server") 772 self.ssh.run("uci delete dhcp.lan.ndp") 773 774 self.ssh.run("uci delete dhcp.wan6") 775 776 self.service_manager.need_restart(SERVICE_ODHCPD) 777 self.commit_changes() 778 779 def _add_dhcp_option(self, args): 780 self.ssh.run("uci add_list dhcp.lan.dhcp_option=\"%s\"" % args) 781 782 def _remove_dhcp_option(self, args): 783 self.ssh.run("uci del_list dhcp.lan.dhcp_option=\"%s\"" % args) 784 785 def add_default_dns(self, addr_list): 786 """Add default dns server for client. 787 788 Args: 789 addr_list: dns ip address for Openwrt client. 790 """ 791 self._add_dhcp_option("6,%s" % ",".join(addr_list)) 792 self.config.add("default_dns %s" % addr_list) 793 self.service_manager.need_restart(SERVICE_DNSMASQ) 794 self.commit_changes() 795 796 def del_default_dns(self, addr_list): 797 """Remove default dns server for client. 798 799 Args: 800 addr_list: list of dns ip address for Openwrt client. 801 """ 802 self._remove_dhcp_option("6,%s" % addr_list) 803 self.config.discard("default_dns %s" % addr_list) 804 self.service_manager.need_restart(SERVICE_DNSMASQ) 805 self.commit_changes() 806 807 def add_default_v6_dns(self, addr_list): 808 """Add default v6 dns server for client. 809 810 Args: 811 addr_list: dns ip address for Openwrt client. 812 """ 813 self.ssh.run("uci add_list dhcp.lan.dns=\"%s\"" % addr_list) 814 self.config.add("default_v6_dns %s" % addr_list) 815 self.service_manager.need_restart(SERVICE_ODHCPD) 816 self.commit_changes() 817 818 def del_default_v6_dns(self, addr_list): 819 """Del default v6 dns server for client. 820 821 Args: 822 addr_list: dns ip address for Openwrt client. 823 """ 824 self.ssh.run("uci del_list dhcp.lan.dns=\"%s\"" % addr_list) 825 self.config.add("default_v6_dns %s" % addr_list) 826 self.service_manager.need_restart(SERVICE_ODHCPD) 827 self.commit_changes() 828 829 def add_ipv6_prefer_option(self): 830 self._add_dhcp_option("108,1800i") 831 self.config.add("ipv6_prefer_option") 832 self.service_manager.need_restart(SERVICE_DNSMASQ) 833 self.commit_changes() 834 835 def remove_ipv6_prefer_option(self): 836 self._remove_dhcp_option("108,1800i") 837 self.config.discard("ipv6_prefer_option") 838 self.service_manager.need_restart(SERVICE_DNSMASQ) 839 self.commit_changes() 840 841 def start_tcpdump(self, test_name, args="", interface="br-lan"): 842 """"Start tcpdump on OpenWrt. 843 844 Args: 845 test_name: Test name for create tcpdump file name. 846 args: Option args for tcpdump. 847 interface: Interface to logging. 848 Returns: 849 tcpdump_file_name: tcpdump file name on OpenWrt. 850 pid: tcpdump process id. 851 """ 852 if not self.path_exists(TCPDUMP_DIR): 853 self.ssh.run("mkdir %s" % TCPDUMP_DIR) 854 tcpdump_file_name = "openwrt_%s_%s.pcap" % (test_name, 855 time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time()))) 856 tcpdump_file_path = "".join([TCPDUMP_DIR, tcpdump_file_name]) 857 cmd = "tcpdump -i %s -s0 %s -w %s" % (interface, args, tcpdump_file_path) 858 self.ssh.run_async(cmd) 859 pid = self._get_tcpdump_pid(tcpdump_file_name) 860 if not pid: 861 raise signals.TestFailure("Fail to start tcpdump on OpenWrt.") 862 # Set delay to prevent tcpdump fail to capture target packet. 863 time.sleep(15) 864 return tcpdump_file_name 865 866 def stop_tcpdump(self, tcpdump_file_name, pull_dir=None): 867 """Stop tcpdump on OpenWrt and pull the pcap file. 868 869 Args: 870 tcpdump_file_name: tcpdump file name on OpenWrt. 871 pull_dir: Keep none if no need to pull. 872 Returns: 873 tcpdump abs_path on host. 874 """ 875 # Set delay to prevent tcpdump fail to capture target packet. 876 time.sleep(15) 877 pid = self._get_tcpdump_pid(tcpdump_file_name) 878 self.ssh.run("kill -9 %s" % pid, ignore_status=True) 879 if self.path_exists(TCPDUMP_DIR) and pull_dir: 880 tcpdump_path = "".join([TCPDUMP_DIR, tcpdump_file_name]) 881 tcpdump_remote_path = "/".join([pull_dir, tcpdump_file_name]) 882 tcpdump_local_path = "%s@%s:%s" % (self.user, self.ip, tcpdump_path) 883 utils.exe_cmd("scp %s %s" % (tcpdump_local_path, tcpdump_remote_path)) 884 885 if self._get_tcpdump_pid(tcpdump_file_name): 886 raise signals.TestFailure("Failed to stop tcpdump on OpenWrt.") 887 if self.file_exists(tcpdump_path): 888 self.ssh.run("rm -f %s" % tcpdump_path) 889 return tcpdump_remote_path if pull_dir else None 890 891 def clear_tcpdump(self): 892 self.ssh.run("killall tpcdump", ignore_status=True) 893 if self.ssh.run("pgrep tpcdump", ignore_status=True).stdout: 894 raise signals.TestFailure("Failed to clean up tcpdump process.") 895 896 def _get_tcpdump_pid(self, tcpdump_file_name): 897 """Check tcpdump process on OpenWrt.""" 898 return self.ssh.run("pgrep -f %s" % (tcpdump_file_name), ignore_status=True).stdout 899 900 def setup_mdns(self): 901 self.config.add("setup_mdns") 902 self.package_install(MDNS_PACKAGE) 903 self.commit_changes() 904 905 def remove_mdns(self): 906 self.config.discard("setup_mdns") 907 self.package_remove(MDNS_PACKAGE) 908 self.commit_changes() 909 910 def block_dns_response(self): 911 self.config.add("block_dns_response") 912 iptable_rules = list(network_const.FIREWALL_RULES_DISABLE_DNS_RESPONSE) 913 self.add_custom_firewall_rules(iptable_rules) 914 self.service_manager.need_restart(SERVICE_FIREWALL) 915 self.commit_changes() 916 917 def unblock_dns_response(self): 918 self.config.discard("block_dns_response") 919 self.remove_custom_firewall_rules() 920 self.service_manager.need_restart(SERVICE_FIREWALL) 921 self.commit_changes() 922 923 def setup_captive_portal(self): 924 self.package_install(CAPTIVE_PORTAL_PACKAGE) 925 self.config.add("setup_captive_portal") 926 self.service_manager.need_restart(SERVICE_NODOGSPLASH) 927 self.commit_changes() 928 929 def remove_cpative_portal(self): 930 self.package_remove(CAPTIVE_PORTAL_PACKAGE) 931 self.config.discard("setup_captive_portal") 932 self.commit_changes() 933 934 935class ServiceManager(object): 936 """Class for service on OpenWrt. 937 938 Attributes: 939 ssh: ssh object for the AP. 940 _need_restart: Record service need to restart. 941 """ 942 943 def __init__(self, ssh): 944 self.ssh = ssh 945 self._need_restart = set() 946 947 def enable(self, service_name): 948 """Enable service auto start.""" 949 self.ssh.run("/etc/init.d/%s enable" % service_name) 950 951 def disable(self, service_name): 952 """Disable service auto start.""" 953 self.ssh.run("/etc/init.d/%s disable" % service_name) 954 955 def restart(self, service_name): 956 """Restart the service.""" 957 self.ssh.run("/etc/init.d/%s restart" % service_name) 958 959 def reload(self, service_name): 960 """Restart the service.""" 961 self.ssh.run("/etc/init.d/%s reload" % service_name) 962 963 def restart_services(self): 964 """Restart all services need to restart.""" 965 for service in self._need_restart: 966 if service == SERVICE_NETWORK: 967 self.reload(service) 968 self.restart(service) 969 self._need_restart = set() 970 971 def stop(self, service_name): 972 """Stop the service.""" 973 self.ssh.run("/etc/init.d/%s stop" % service_name) 974 975 def need_restart(self, service_name): 976 self._need_restart.add(service_name) 977