• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16from acts.controllers.openwrt_lib import network_const
17
18SERVICE_DNSMASQ = "dnsmasq"
19SERVICE_STUNNEL = "stunnel"
20SERVICE_NETWORK = "network"
21SERVICE_PPTPD = "pptpd"
22SERVICE_FIREWALL = "firewall"
23SERVICE_IPSEC = "ipsec"
24SERVICE_XL2TPD = "xl2tpd"
25PPTP_PACKAGE = "pptpd kmod-nf-nathelper-extra"
26L2TP_PACKAGE = "strongswan-full openssl-util xl2tpd"
27STUNNEL_CONFIG_PATH = "/etc/stunnel/DoTServer.conf"
28HISTORY_CONFIG_PATH = "/etc/dirty_configs"
29PPTPD_OPTION_PATH = "/etc/ppp/options.pptpd"
30XL2TPD_CONFIG_PATH = "/etc/xl2tpd/xl2tpd.conf"
31XL2TPD_OPTION_CONFIG_PATH = "/etc/ppp/options.xl2tpd"
32FIREWALL_CUSTOM_OPTION_PATH = "/etc/firewall.user"
33PPP_CHAP_SECRET_PATH = "/etc/ppp/chap-secrets"
34LOCALHOST = "192.168.1.1"
35DEFAULT_PACKAGE_INSTALL_TIMEOUT = 200
36
37
38class NetworkSettings(object):
39    """Class for network settings.
40
41    Attributes:
42        ssh: ssh connection object.
43        service_manager: Object manage service configuration
44        ip: ip address for AccessPoint.
45        log: Logging object for AccessPoint.
46        config: A list to store changes on network settings.
47        firewall_rules_list: A list of firewall rule name list
48        cleanup_map: A dict for compare oppo functions.
49        l2tp: profile for vpn l2tp server.
50    """
51
52    def __init__(self, ssh, ip, logger):
53        """Initialize wireless settings.
54
55        Args:
56            ssh: ssh connection object.
57            ip: ip address for AccessPoint.
58            logger: Logging object for AccessPoint.
59        """
60        self.ssh = ssh
61        self.service_manager = ServiceManager(ssh)
62        self.ip = ip
63        self.log = logger
64        self.config = set()
65        self.firewall_rules_list = []
66        self.cleanup_map = {
67            "setup_dns_server": self.remove_dns_server,
68            "setup_vpn_pptp_server": self.remove_vpn_pptp_server,
69            "setup_vpn_l2tp_server": self.remove_vpn_l2tp_server,
70            "disable_ipv6": self.enable_ipv6
71        }
72        # This map contains cleanup functions to restore the configuration to
73        # its default state. We write these keys to HISTORY_CONFIG_PATH prior to
74        # making any changes to that subsystem.
75        # This makes it easier to recover after an aborted test.
76        self.update_firewall_rules_list()
77        self.cleanup_network_settings()
78
79    def cleanup_network_settings(self):
80        """Reset all changes on Access point."""
81
82        # Detect if any changes that is not clean up.
83        if self.file_exists(HISTORY_CONFIG_PATH):
84            out = self.ssh.run("cat %s" % HISTORY_CONFIG_PATH).stdout
85            if out:
86                self.config = set(out.split("\n"))
87
88        if self.config:
89            temp = self.config.copy()
90            for change in temp:
91                self.cleanup_map[change]()
92            self.config = set()
93
94        if self.file_exists(HISTORY_CONFIG_PATH):
95            out = self.ssh.run("cat %s" % HISTORY_CONFIG_PATH).stdout
96            if not out:
97                self.ssh.run("rm %s" % HISTORY_CONFIG_PATH)
98
99    def commit_changes(self):
100        """Apply changes on Access point."""
101        self.ssh.run("uci commit")
102        self.service_manager.restart_services()
103        self.create_config_file("\n".join(self.config),
104                                HISTORY_CONFIG_PATH)
105
106    def package_install(self, package_list):
107        """Install packages on OpenWrtAP via opkg If not installed.
108
109        Args:
110            package_list: package list to install.
111                          e.g. "pptpd kmod-mppe kmod-nf-nathelper-extra"
112        """
113        self.ssh.run("opkg update")
114        for package_name in package_list.split(" "):
115            if not self._package_installed(package_name):
116                self.ssh.run("opkg install %s" % package_name,
117                             timeout=DEFAULT_PACKAGE_INSTALL_TIMEOUT)
118                self.log.info("Package: %s installed." % package_name)
119            else:
120                self.log.info("Package: %s skipped (already installed)." % package_name)
121
122    def package_remove(self, package_list):
123        """Remove packages on OpenWrtAP via opkg If existed.
124
125        Args:
126            package_list: package list to remove.
127        """
128        for package_name in package_list.split(" "):
129            if self._package_installed(package_name):
130                self.ssh.run("opkg remove %s" % package_name)
131                self.log.info("Package: %s removed." % package_name)
132            else:
133                self.log.info("No exist package %s found." % package_name)
134
135    def _package_installed(self, package_name):
136        """Check if target package installed on OpenWrtAP.
137
138        Args:
139            package_name: package name want to check.
140
141        Returns:
142            True if installed.
143        """
144        if self.ssh.run("opkg list-installed %s" % package_name).stdout:
145            return True
146        return False
147
148    def file_exists(self, abs_file_path):
149        """Check if target file exist on specific path on OpenWrt.
150
151        Args:
152            abs_file_path: Absolute path for the file.
153
154        Returns:
155            True if Existed.
156        """
157        path, file_name = abs_file_path.rsplit("/", 1)
158        if self.ssh.run("ls %s | grep %s" % (path, file_name),
159                        ignore_status=True).stdout:
160            return True
161        return False
162
163    def path_exists(self, abs_path):
164        """Check if dir exist on OpenWrt."""
165        try:
166            self.ssh.run("ls %s" % abs_path)
167        except:
168            return False
169        return True
170
171    def count(self, config, key):
172        """Count in uci config.
173
174        Args:
175            config: config or section to research
176            key: keywords to  e.g. rule, domain
177        Returns:
178            Numbers of the count.
179        """
180        count = self.ssh.run("uci show %s | grep =%s" % (config, key),
181                             ignore_status=True).stdout
182        return len(count.split("\n"))
183
184    def create_config_file(self, config, file_path):
185        """Create config file. Overwrite if file already exist.
186
187        Args:
188            config: A string of content of config.
189            file_path: Config's abs_path.
190        """
191        self.ssh.run("echo -e \"%s\" > %s" % (config, file_path))
192
193    def replace_config_option(self, old_option, new_option, file_path):
194        """Replace config option if pattern match.
195
196        If find match pattern with old_option, then replace it with new_option.
197        Else add new_option to the file.
198
199        Args:
200            old_option: the regexp pattern to replace.
201            new_option: the option to add.
202            file_path: Config's abs_path.
203        """
204        config = self.ssh.run("cat %s" % file_path).stdout
205        config, count = re.subn(old_option, new_option, config)
206        if not count:
207            config = "\n".join([config, new_option])
208        self.create_config_file(config, file_path)
209
210    def remove_config_option(self, option, file_path):
211        """Remove option from config file.
212
213        Args:
214            option: Option to remove. Support regular expression.
215            file_path: Config's abs_path.
216        Returns:
217            Boolean for find option to remove.
218        """
219        config = self.ssh.run("cat %s" % file_path).stdout.split("\n")
220        for line in config:
221            count = re.subn(option, "", line)[1]
222            if count > 0:
223                config.remove(line)
224                self.create_config_file("\n".join(config), file_path)
225                return True
226        self.log.warning("No match option to remove.")
227        return False
228
229    def setup_dns_server(self, domain_name):
230        """Setup DNS server on OpenWrtAP.
231
232        Args:
233            domain_name: Local dns domain name.
234        """
235        self.config.add("setup_dns_server")
236        self.log.info("Setup DNS server with domain name %s" % domain_name)
237        self.ssh.run("uci set dhcp.@dnsmasq[0].local='/%s/'" % domain_name)
238        self.ssh.run("uci set dhcp.@dnsmasq[0].domain='%s'" % domain_name)
239        self.add_resource_record(domain_name, self.ip)
240        self.service_manager.need_restart(SERVICE_DNSMASQ)
241        self.commit_changes()
242
243        # Check stunnel package is installed
244        self.package_install("stunnel")
245        self.service_manager.stop(SERVICE_STUNNEL)
246        self.service_manager.disable(SERVICE_STUNNEL)
247
248        # Enable stunnel
249        self.create_stunnel_config()
250        self.ssh.run("stunnel /etc/stunnel/DoTServer.conf")
251
252    def remove_dns_server(self):
253        """Remove DNS server on OpenWrtAP."""
254        if self.file_exists("/var/run/stunnel.pid"):
255            self.ssh.run("kill $(cat /var/run/stunnel.pid)")
256        self.ssh.run("uci set dhcp.@dnsmasq[0].local='/lan/'")
257        self.ssh.run("uci set dhcp.@dnsmasq[0].domain='lan'")
258        self.clear_resource_record()
259        self.service_manager.need_restart(SERVICE_DNSMASQ)
260        self.config.discard("setup_dns_server")
261        self.commit_changes()
262
263    def add_resource_record(self, domain_name, domain_ip):
264        """Add resource record.
265
266        Args:
267            domain_name: A string for domain name.
268            domain_ip: A string for domain ip.
269        """
270        self.ssh.run("uci add dhcp domain")
271        self.ssh.run("uci set dhcp.@domain[-1].name='%s'" % domain_name)
272        self.ssh.run("uci set dhcp.@domain[-1].ip='%s'" % domain_ip)
273        self.service_manager.need_restart(SERVICE_DNSMASQ)
274
275    def del_resource_record(self):
276        """Delete the last resource record."""
277        self.ssh.run("uci delete dhcp.@domain[-1]")
278        self.service_manager.need_restart(SERVICE_DNSMASQ)
279
280    def clear_resource_record(self):
281        """Delete the all resource record."""
282        rr = self.ssh.run("uci show dhcp | grep =domain",
283                          ignore_status=True).stdout
284        if rr:
285            for _ in rr.split("\n"):
286                self.del_resource_record()
287        self.service_manager.need_restart(SERVICE_DNSMASQ)
288
289    def create_stunnel_config(self):
290        """Create config for stunnel service."""
291        stunnel_config = [
292            "pid = /var/run/stunnel.pid",
293            "[dns]",
294            "accept = 853",
295            "connect = 127.0.0.1:53",
296            "cert = /etc/stunnel/fullchain.pem",
297            "key = /etc/stunnel/privkey.pem",
298        ]
299        config_string = "\n".join(stunnel_config)
300        self.create_config_file(config_string, STUNNEL_CONFIG_PATH)
301
302    def setup_vpn_pptp_server(self, local_ip, user, password):
303        """Setup pptp vpn server on OpenWrt.
304
305        Args:
306            local_ip: local pptp server ip address.
307            user: username for pptp user.
308            password: password for pptp user.
309        """
310        #  Install pptp service
311        self.package_install(PPTP_PACKAGE)
312
313        self.config.add("setup_vpn_pptp_server")
314        # Edit /etc/config/pptpd & /etc/ppp/options.pptpd
315        self.setup_pptpd(local_ip, user, password)
316        # Edit /etc/config/firewall & /etc/firewall.user
317        self.setup_firewall_rules_for_pptp()
318        # Enable service
319        self.service_manager.enable(SERVICE_PPTPD)
320        self.service_manager.need_restart(SERVICE_PPTPD)
321        self.service_manager.need_restart(SERVICE_FIREWALL)
322        self.commit_changes()
323
324    def remove_vpn_pptp_server(self):
325        """Remove pptp vpn server on OpenWrt."""
326        # Edit /etc/config/pptpd
327        self.restore_pptpd()
328        # Edit /etc/config/firewall & /etc/firewall.user
329        self.restore_firewall_rules_for_pptp()
330        # Disable service
331        self.service_manager.disable(SERVICE_PPTPD)
332        self.service_manager.need_restart(SERVICE_PPTPD)
333        self.service_manager.need_restart(SERVICE_FIREWALL)
334        self.config.discard("setup_vpn_pptp_server")
335        self.commit_changes()
336
337        self.package_remove(PPTP_PACKAGE)
338        self.ssh.run("rm /etc/ppp/options.pptpd")
339        self.ssh.run("rm /etc/config/pptpd")
340
341    def setup_pptpd(self, local_ip, username, password, ms_dns="8.8.8.8"):
342        """Setup pptpd config for ip addr and account.
343
344        Args:
345            local_ip: vpn server address
346            username: pptp vpn username
347            password: pptp vpn password
348            ms_dns: DNS server
349        """
350        # Calculate remote ip address
351        # e.g. local_ip = 10.10.10.9
352        # remote_ip = 10.10.10.10 -250
353        remote_ip = local_ip.split(".")
354        remote_ip.append(str(int(remote_ip.pop(-1)) + 1))
355        remote_ip = ".".join(remote_ip)
356        # Enable pptp service and set ip addr
357        self.ssh.run("uci set pptpd.pptpd.enabled=1")
358        self.ssh.run("uci set pptpd.pptpd.localip='%s'" % local_ip)
359        self.ssh.run("uci set pptpd.pptpd.remoteip='%s-250'" % remote_ip)
360
361        # Setup pptp service account
362        self.ssh.run("uci set pptpd.@login[0].username='%s'" % username)
363        self.ssh.run("uci set pptpd.@login[0].password='%s'" % password)
364        self.service_manager.need_restart(SERVICE_PPTPD)
365
366        self.replace_config_option(r"#*ms-dns \d+.\d+.\d+.\d+",
367                                   "ms-dns %s" % ms_dns, PPTPD_OPTION_PATH)
368        self.replace_config_option("(#no)*proxyarp",
369                                   "proxyarp", PPTPD_OPTION_PATH)
370
371    def restore_pptpd(self):
372        """Disable pptpd."""
373        self.ssh.run("uci set pptpd.pptpd.enabled=0")
374        self.remove_config_option(r"\S+ pptp-server \S+ \*",
375                                  PPP_CHAP_SECRET_PATH)
376        self.service_manager.need_restart(SERVICE_PPTPD)
377
378    def setup_vpn_l2tp_server(self,
379                              vpn_server_hostname,
380                              vpn_server_address,
381                              vpn_username,
382                              vpn_password,
383                              psk_secret,
384                              server_name,
385                              country,
386                              org):
387        """Setup l2tp vpn server on OpenWrt.
388
389        Args:
390            vpn_server_hostname: vpn server domain name
391            vpn_server_address: vpn server addr
392            vpn_username: vpn account
393            vpn_password: vpn password
394            psk_secret: psk for ipsec
395            server_name: vpn server name for register in OpenWrt
396            country: country code for generate cert keys.
397            org: Organization name for generate cert keys.
398        """
399        self.l2tp = network_const.VpnL2tp(vpn_server_hostname,
400                                           vpn_server_address,
401                                           vpn_username,
402                                           vpn_password,
403                                           psk_secret,
404                                           server_name)
405
406        self.package_install(L2TP_PACKAGE)
407        self.config.add("setup_vpn_l2tp_server")
408
409        # /etc/strongswan.conf: Strongswan configuration file
410        self.setup_strongswan()
411        # /etc/ipsec.conf /etc/ipsec.secrets
412        self.setup_ipsec()
413        # /etc/xl2tpd/xl2tpd.conf & /etc/ppp/options.xl2tpd
414        self.setup_xl2tpd()
415        # /etc/ppp/chap-secrets
416        self.setup_ppp_secret()
417        # /etc/config/firewall & /etc/firewall.user
418        self.setup_firewall_rules_for_l2tp()
419        # generate cert and key for rsa
420        self.generate_vpn_cert_keys(country, org)
421        # restart service
422        self.service_manager.need_restart(SERVICE_IPSEC)
423        self.service_manager.need_restart(SERVICE_XL2TPD)
424        self.service_manager.need_restart(SERVICE_FIREWALL)
425        self.commit_changes()
426
427    def remove_vpn_l2tp_server(self):
428        """Remove l2tp vpn server on OpenWrt."""
429        self.config.discard("setup_vpn_l2tp_server")
430        self.restore_firewall_rules_for_l2tp()
431        self.service_manager.need_restart(SERVICE_IPSEC)
432        self.service_manager.need_restart(SERVICE_XL2TPD)
433        self.service_manager.need_restart(SERVICE_FIREWALL)
434        self.commit_changes()
435        self.package_remove(L2TP_PACKAGE)
436        if hasattr(self, "l2tp"):
437            delattr(self, "l2tp")
438
439    def setup_strongswan(self, dns="8.8.8.8"):
440        """Setup strongswan config."""
441        config = [
442            "charon {",
443            "   load_modular = yes",
444            "   plugins {",
445            "       include strongswan.d/charon/*.conf",
446            "   }",
447            "   dns1=%s" % dns,
448            "}"
449        ]
450        self.create_config_file("\n".join(config), "/etc/strongswan.conf")
451
452    def setup_ipsec(self):
453        """Setup ipsec config."""
454        def load_config(data):
455            for i in data.keys():
456                config.append(i)
457                for j in data[i].keys():
458                    config.append("\t %s=%s" % (j, data[i][j]))
459                config.append("")
460
461        config = []
462        load_config(network_const.IPSEC_CONF)
463        load_config(network_const.IPSEC_L2TP_PSK)
464        load_config(network_const.IPSEC_L2TP_RSA)
465        self.create_config_file("\n".join(config), "/etc/ipsec.conf")
466
467        ipsec_secret = []
468        ipsec_secret.append(r": PSK \"%s\"" % self.l2tp.psk_secret)
469        ipsec_secret.append(r": RSA \"%s\"" % "serverKey.der")
470        self.create_config_file("\n".join(ipsec_secret), "/etc/ipsec.secrets")
471
472    def setup_xl2tpd(self, ip_range=20):
473        """Setup xl2tpd config."""
474        net_id, host_id = self.l2tp.address.rsplit(".", 1)
475        xl2tpd_conf = network_const.XL2TPD_CONF_GLOBAL
476        xl2tpd_conf.append("auth file = %s" % PPP_CHAP_SECRET_PATH)
477        xl2tpd_conf.extend(network_const.XL2TPD_CONF_INS)
478        xl2tpd_conf.append("ip range = %s.%s-%s.%s" %
479                           (net_id, host_id, net_id,
480                            str(int(host_id)+ip_range)))
481        xl2tpd_conf.append("local ip = %s" % self.l2tp.address)
482        xl2tpd_conf.append("name = %s" % self.l2tp.name)
483        xl2tpd_conf.append("pppoptfile = %s" % XL2TPD_OPTION_CONFIG_PATH)
484
485        self.create_config_file("\n".join(xl2tpd_conf), XL2TPD_CONFIG_PATH)
486        xl2tpd_option = network_const.XL2TPD_OPTION
487        xl2tpd_option.append("name %s" % self.l2tp.name)
488        self.create_config_file("\n".join(xl2tpd_option),
489                                XL2TPD_OPTION_CONFIG_PATH)
490
491    def setup_ppp_secret(self):
492        self.replace_config_option(
493            r"\S+ %s \S+ \*" % self.l2tp.name,
494            "%s %s %s *" % (self.l2tp.username,
495                            self.l2tp.name,
496                            self.l2tp.password),
497            PPP_CHAP_SECRET_PATH)
498
499    def generate_vpn_cert_keys(self, country, org):
500        """Generate cert and keys for vpn server."""
501        rsa = "--type rsa"
502        lifetime = "--lifetime 365"
503        size = "--size 4096"
504
505        self.ssh.run("ipsec pki --gen %s %s --outform der > caKey.der" %
506                     (rsa, size))
507        self.ssh.run("ipsec pki --self --ca %s --in caKey.der %s --dn "
508                     "\"C=%s, O=%s, CN=%s\" --outform der > caCert.der" %
509                     (lifetime, rsa, country, org, self.l2tp.hostname))
510        self.ssh.run("ipsec pki --gen %s %s --outform der > serverKey.der" %
511                     (size, rsa))
512        self.ssh.run("ipsec pki --pub --in serverKey.der %s | ipsec pki "
513                     "--issue %s --cacert caCert.der --cakey caKey.der "
514                     "--dn \"C=%s, O=%s, CN=%s\" --san %s --flag serverAuth"
515                     " --flag ikeIntermediate --outform der > serverCert.der" %
516                     (rsa, lifetime, country, org, self.l2tp.hostname, LOCALHOST))
517        self.ssh.run("ipsec pki --gen %s %s --outform der > clientKey.der" %
518                     (size, rsa))
519        self.ssh.run("ipsec pki --pub --in clientKey.der %s | ipsec pki "
520                     "--issue %s --cacert caCert.der --cakey caKey.der "
521                     "--dn \"C=%s, O=%s, CN=%s@%s\" --outform der > "
522                     "clientCert.der" % (rsa, lifetime, country, org,
523                                         self.l2tp.username, self.l2tp.hostname))
524
525        self.ssh.run(
526            "openssl rsa -inform DER -in clientKey.der"
527            " -out clientKey.pem -outform PEM"
528        )
529        self.ssh.run(
530            "openssl x509 -inform DER -in clientCert.der"
531            " -out clientCert.pem -outform PEM"
532        )
533        self.ssh.run(
534            "openssl x509 -inform DER -in caCert.der"
535            " -out caCert.pem -outform PEM"
536        )
537        self.ssh.run(
538            "openssl pkcs12 -in clientCert.pem -inkey  clientKey.pem"
539            " -certfile caCert.pem -export -out clientPkcs.p12 -passout pass:"
540        )
541
542        self.ssh.run("mv caCert.pem /etc/ipsec.d/cacerts/")
543        self.ssh.run("mv *Cert* /etc/ipsec.d/certs/")
544        self.ssh.run("mv *Key* /etc/ipsec.d/private/")
545        if not self.path_exists("/www/downloads/"):
546            self.ssh.run("mkdir /www/downloads/")
547        self.ssh.run("mv clientPkcs.p12 /www/downloads/")
548        self.ssh.run("chmod 664 /www/downloads/clientPkcs.p12")
549
550    def update_firewall_rules_list(self):
551        """Update rule list in /etc/config/firewall."""
552        new_rules_list = []
553        for i in range(self.count("firewall", "rule")):
554            rule = self.ssh.run("uci get firewall.@rule[%s].name" % i).stdout
555            new_rules_list.append(rule)
556        self.firewall_rules_list = new_rules_list
557
558    def setup_firewall_rules_for_pptp(self):
559        """Setup firewall for vpn pptp server."""
560        self.update_firewall_rules_list()
561        if "pptpd" not in self.firewall_rules_list:
562            self.ssh.run("uci add firewall rule")
563            self.ssh.run("uci set firewall.@rule[-1].name='pptpd'")
564            self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'")
565            self.ssh.run("uci set firewall.@rule[-1].proto='tcp'")
566            self.ssh.run("uci set firewall.@rule[-1].dest_port='1723'")
567            self.ssh.run("uci set firewall.@rule[-1].family='ipv4'")
568            self.ssh.run("uci set firewall.@rule[-1].src='wan'")
569
570        if "GRP" not in self.firewall_rules_list:
571            self.ssh.run("uci add firewall rule")
572            self.ssh.run("uci set firewall.@rule[-1].name='GRP'")
573            self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'")
574            self.ssh.run("uci set firewall.@rule[-1].src='wan'")
575            self.ssh.run("uci set firewall.@rule[-1].proto='47'")
576
577        iptable_rules = network_const.FIREWALL_RULES_FOR_PPTP
578        self.add_custom_firewall_rules(iptable_rules)
579        self.service_manager.need_restart(SERVICE_FIREWALL)
580
581    def restore_firewall_rules_for_pptp(self):
582        """Restore firewall for vpn pptp server."""
583        self.update_firewall_rules_list()
584        if "pptpd" in self.firewall_rules_list:
585            self.ssh.run("uci del firewall.@rule[%s]"
586                         % self.firewall_rules_list.index("pptpd"))
587        self.update_firewall_rules_list()
588        if "GRP" in self.firewall_rules_list:
589            self.ssh.run("uci del firewall.@rule[%s]"
590                         % self.firewall_rules_list.index("GRP"))
591        self.remove_custom_firewall_rules()
592        self.service_manager.need_restart(SERVICE_FIREWALL)
593
594    def setup_firewall_rules_for_l2tp(self):
595        """Setup firewall for vpn l2tp server."""
596        self.update_firewall_rules_list()
597        if "ipsec esp" not in self.firewall_rules_list:
598            self.ssh.run("uci add firewall rule")
599            self.ssh.run("uci set firewall.@rule[-1].name='ipsec esp'")
600            self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'")
601            self.ssh.run("uci set firewall.@rule[-1].proto='esp'")
602            self.ssh.run("uci set firewall.@rule[-1].src='wan'")
603
604        if "ipsec nat-t" not in self.firewall_rules_list:
605            self.ssh.run("uci add firewall rule")
606            self.ssh.run("uci set firewall.@rule[-1].name='ipsec nat-t'")
607            self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'")
608            self.ssh.run("uci set firewall.@rule[-1].src='wan'")
609            self.ssh.run("uci set firewall.@rule[-1].proto='udp'")
610            self.ssh.run("uci set firewall.@rule[-1].dest_port='4500'")
611
612        if "auth header" not in self.firewall_rules_list:
613            self.ssh.run("uci add firewall rule")
614            self.ssh.run("uci set firewall.@rule[-1].name='auth header'")
615            self.ssh.run("uci set firewall.@rule[-1].target='ACCEPT'")
616            self.ssh.run("uci set firewall.@rule[-1].src='wan'")
617            self.ssh.run("uci set firewall.@rule[-1].proto='ah'")
618
619        net_id = self.l2tp.address.rsplit(".", 1)[0]
620        iptable_rules = network_const.FIREWALL_RULES_FOR_L2TP
621        iptable_rules.append("iptables -A FORWARD -s %s.0/24"
622                             "  -j ACCEPT" % net_id)
623        iptable_rules.append("iptables -t nat -A POSTROUTING"
624                             " -s %s.0/24 -o eth0.2 -j MASQUERADE" % net_id)
625
626        self.add_custom_firewall_rules(iptable_rules)
627        self.service_manager.need_restart(SERVICE_FIREWALL)
628
629    def restore_firewall_rules_for_l2tp(self):
630        """Restore firewall for vpn l2tp server."""
631        self.update_firewall_rules_list()
632        if "ipsec esp" in self.firewall_rules_list:
633            self.ssh.run("uci del firewall.@rule[%s]"
634                         % self.firewall_rules_list.index("ipsec esp"))
635        self.update_firewall_rules_list()
636        if "ipsec nat-t" in self.firewall_rules_list:
637            self.ssh.run("uci del firewall.@rule[%s]"
638                         % self.firewall_rules_list.index("ipsec nat-t"))
639        self.update_firewall_rules_list()
640        if "auth header" in self.firewall_rules_list:
641            self.ssh.run("uci del firewall.@rule[%s]"
642                         % self.firewall_rules_list.index("auth header"))
643        self.remove_custom_firewall_rules()
644        self.service_manager.need_restart(SERVICE_FIREWALL)
645
646    def add_custom_firewall_rules(self, rules):
647        """Backup current custom rules and replace with arguments.
648
649        Args:
650            rules: A list of iptable rules to apply.
651        """
652        backup_file_path = FIREWALL_CUSTOM_OPTION_PATH+".backup"
653        if not self.file_exists(backup_file_path):
654            self.ssh.run("mv %s %s" % (FIREWALL_CUSTOM_OPTION_PATH,
655                                       backup_file_path))
656        for rule in rules:
657            self.ssh.run("echo %s >> %s" % (rule, FIREWALL_CUSTOM_OPTION_PATH))
658
659    def remove_custom_firewall_rules(self):
660        """Clean up and recover custom firewall rules."""
661        backup_file_path = FIREWALL_CUSTOM_OPTION_PATH+".backup"
662        if self.file_exists(backup_file_path):
663            self.ssh.run("mv %s %s" % (backup_file_path,
664                                       FIREWALL_CUSTOM_OPTION_PATH))
665        else:
666            self.log.debug("Did not find %s" % backup_file_path)
667            self.ssh.run("echo "" > %s" % FIREWALL_CUSTOM_OPTION_PATH)
668
669    def disable_pptp_service(self):
670        """Disable pptp service."""
671        self.package_remove(PPTP_PACKAGE)
672
673    def enable_ipv6(self):
674        """Enable ipv6 on OpenWrt."""
675        self.ssh.run("uci set network.lan.ipv6=1")
676        self.ssh.run("uci set network.wan.ipv6=1")
677        self.service_manager.enable("odhcpd")
678        self.service_manager.reload(SERVICE_NETWORK)
679        self.config.discard("disable_ipv6")
680        self.commit_changes()
681
682    def disable_ipv6(self):
683        """Disable ipv6 on OpenWrt."""
684        self.config.add("disable_ipv6")
685        self.ssh.run("uci set network.lan.ipv6=0")
686        self.ssh.run("uci set network.wan.ipv6=0")
687        self.service_manager.disable("odhcpd")
688        self.service_manager.reload(SERVICE_NETWORK)
689        self.commit_changes()
690
691
692class ServiceManager(object):
693    """Class for service on OpenWrt.
694
695        Attributes:
696        ssh: ssh object for the AP.
697        _need_restart: Record service need to restart.
698    """
699
700    def __init__(self, ssh):
701        self.ssh = ssh
702        self._need_restart = set()
703
704    def enable(self, service_name):
705        """Enable service auto start."""
706        self.ssh.run("/etc/init.d/%s enable" % service_name)
707
708    def disable(self, service_name):
709        """Disable service auto start."""
710        self.ssh.run("/etc/init.d/%s disable" % service_name)
711
712    def restart(self, service_name):
713        """Restart the service."""
714        self.ssh.run("/etc/init.d/%s restart" % service_name)
715
716    def reload(self, service_name):
717        """Restart the service."""
718        self.ssh.run("/etc/init.d/%s reload" % service_name)
719
720    def restart_services(self):
721        """Restart all services need to restart."""
722        for service in self._need_restart:
723            self.restart(service)
724        self._need_restart = set()
725
726    def stop(self, service_name):
727        """Stop the service."""
728        self.ssh.run("/etc/init.d/%s stop" % service_name)
729
730    def need_restart(self, service_name):
731        self._need_restart.add(service_name)
732