Home | History | Annotate | Download | only in netd
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // #define LOG_NDEBUG 0
     18 
     19 #include <stdlib.h>
     20 #include <sys/socket.h>
     21 #include <sys/types.h>
     22 #include <netinet/in.h>
     23 #include <arpa/inet.h>
     24 #include <dirent.h>
     25 #include <errno.h>
     26 #include <string.h>
     27 #include <fcntl.h>
     28 #include <linux/if.h>
     29 
     30 #define LOG_TAG "CommandListener"
     31 
     32 #include <cutils/log.h>
     33 #include <netutils/ifc.h>
     34 #include <sysutils/SocketClient.h>
     35 
     36 #include "CommandListener.h"
     37 #include "ResponseCode.h"
     38 #include "ThrottleController.h"
     39 #include "BandwidthController.h"
     40 #include "SecondaryTableController.h"
     41 
     42 
     43 TetherController *CommandListener::sTetherCtrl = NULL;
     44 NatController *CommandListener::sNatCtrl = NULL;
     45 PppController *CommandListener::sPppCtrl = NULL;
     46 PanController *CommandListener::sPanCtrl = NULL;
     47 SoftapController *CommandListener::sSoftapCtrl = NULL;
     48 BandwidthController * CommandListener::sBandwidthCtrl = NULL;
     49 ResolverController *CommandListener::sResolverCtrl = NULL;
     50 SecondaryTableController *CommandListener::sSecondaryTableCtrl = NULL;
     51 
     52 CommandListener::CommandListener() :
     53                  FrameworkListener("netd") {
     54     registerCmd(new InterfaceCmd());
     55     registerCmd(new IpFwdCmd());
     56     registerCmd(new TetherCmd());
     57     registerCmd(new NatCmd());
     58     registerCmd(new ListTtysCmd());
     59     registerCmd(new PppdCmd());
     60     registerCmd(new PanCmd());
     61     registerCmd(new SoftapCmd());
     62     registerCmd(new BandwidthControlCmd());
     63     registerCmd(new ResolverCmd());
     64 
     65     if (!sSecondaryTableCtrl)
     66         sSecondaryTableCtrl = new SecondaryTableController();
     67     if (!sTetherCtrl)
     68         sTetherCtrl = new TetherController();
     69     if (!sNatCtrl)
     70         sNatCtrl = new NatController(sSecondaryTableCtrl);
     71     if (!sPppCtrl)
     72         sPppCtrl = new PppController();
     73     if (!sPanCtrl)
     74         sPanCtrl = new PanController();
     75     if (!sSoftapCtrl)
     76         sSoftapCtrl = new SoftapController();
     77     if (!sBandwidthCtrl)
     78         sBandwidthCtrl = new BandwidthController();
     79     if (!sResolverCtrl)
     80         sResolverCtrl = new ResolverController();
     81 }
     82 
     83 CommandListener::InterfaceCmd::InterfaceCmd() :
     84                  NetdCommand("interface") {
     85 }
     86 
     87 int CommandListener::writeFile(const char *path, const char *value, int size) {
     88     int fd = open(path, O_WRONLY);
     89     if (fd < 0) {
     90         LOGE("Failed to open %s: %s", path, strerror(errno));
     91         return -1;
     92     }
     93 
     94     if (write(fd, value, size) != size) {
     95         LOGE("Failed to write %s: %s", path, strerror(errno));
     96         close(fd);
     97         return -1;
     98     }
     99     close(fd);
    100     return 0;
    101 }
    102 
    103 int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
    104                                                       int argc, char **argv) {
    105     if (argc < 2) {
    106         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    107         return 0;
    108     }
    109 
    110     if (!strcmp(argv[1], "list")) {
    111         DIR *d;
    112         struct dirent *de;
    113 
    114         if (!(d = opendir("/sys/class/net"))) {
    115             cli->sendMsg(ResponseCode::OperationFailed, "Failed to open sysfs dir", true);
    116             return 0;
    117         }
    118 
    119         while((de = readdir(d))) {
    120             if (de->d_name[0] == '.')
    121                 continue;
    122             cli->sendMsg(ResponseCode::InterfaceListResult, de->d_name, false);
    123         }
    124         closedir(d);
    125         cli->sendMsg(ResponseCode::CommandOkay, "Interface list completed", false);
    126         return 0;
    127     } else if (!strcmp(argv[1], "readrxcounter")) {
    128         if (argc != 3) {
    129             cli->sendMsg(ResponseCode::CommandSyntaxError,
    130                     "Usage: interface readrxcounter <interface>", false);
    131             return 0;
    132         }
    133         unsigned long rx = 0, tx = 0;
    134         if (readInterfaceCounters(argv[2], &rx, &tx)) {
    135             cli->sendMsg(ResponseCode::OperationFailed, "Failed to read counters", true);
    136             return 0;
    137         }
    138 
    139         char *msg;
    140         asprintf(&msg, "%lu", rx);
    141         cli->sendMsg(ResponseCode::InterfaceRxCounterResult, msg, false);
    142         free(msg);
    143 
    144         return 0;
    145     } else if (!strcmp(argv[1], "readtxcounter")) {
    146         if (argc != 3) {
    147             cli->sendMsg(ResponseCode::CommandSyntaxError,
    148                     "Usage: interface readtxcounter <interface>", false);
    149             return 0;
    150         }
    151         unsigned long rx = 0, tx = 0;
    152         if (readInterfaceCounters(argv[2], &rx, &tx)) {
    153             cli->sendMsg(ResponseCode::OperationFailed, "Failed to read counters", true);
    154             return 0;
    155         }
    156 
    157         char *msg = NULL;
    158         asprintf(&msg, "%lu", tx);
    159         cli->sendMsg(ResponseCode::InterfaceTxCounterResult, msg, false);
    160         free(msg);
    161         return 0;
    162     } else if (!strcmp(argv[1], "getthrottle")) {
    163         if (argc != 4 || (argc == 4 && (strcmp(argv[3], "rx") && (strcmp(argv[3], "tx"))))) {
    164             cli->sendMsg(ResponseCode::CommandSyntaxError,
    165                     "Usage: interface getthrottle <interface> <rx|tx>", false);
    166             return 0;
    167         }
    168         int val = 0;
    169         int rc = 0;
    170         int voldRc = ResponseCode::InterfaceRxThrottleResult;
    171 
    172         if (!strcmp(argv[3], "rx")) {
    173             rc = ThrottleController::getInterfaceRxThrottle(argv[2], &val);
    174         } else {
    175             rc = ThrottleController::getInterfaceTxThrottle(argv[2], &val);
    176             voldRc = ResponseCode::InterfaceTxThrottleResult;
    177         }
    178         if (rc) {
    179             cli->sendMsg(ResponseCode::OperationFailed, "Failed to get throttle", true);
    180         } else {
    181             char *msg = NULL;
    182             asprintf(&msg, "%u", val);
    183             cli->sendMsg(voldRc, msg, false);
    184             free(msg);
    185             return 0;
    186         }
    187         return 0;
    188     } else if (!strcmp(argv[1], "setthrottle")) {
    189         if (argc != 5) {
    190             cli->sendMsg(ResponseCode::CommandSyntaxError,
    191                     "Usage: interface setthrottle <interface> <rx_kbps> <tx_kbps>", false);
    192             return 0;
    193         }
    194         if (ThrottleController::setInterfaceThrottle(argv[2], atoi(argv[3]), atoi(argv[4]))) {
    195             cli->sendMsg(ResponseCode::OperationFailed, "Failed to set throttle", true);
    196         } else {
    197             cli->sendMsg(ResponseCode::CommandOkay, "Interface throttling set", false);
    198         }
    199         return 0;
    200     } else {
    201         /*
    202          * These commands take a minimum of 3 arguments
    203          */
    204         if (argc < 3) {
    205             cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    206             return 0;
    207         }
    208 
    209         //     0       1       2        3          4           5     6      7
    210         // interface route add/remove iface default/secondary dest prefix gateway
    211         if (!strcmp(argv[1], "route")) {
    212             int prefix_length = 0;
    213             if (argc < 8) {
    214                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    215                 return 0;
    216             }
    217             if (sscanf(argv[6], "%d", &prefix_length) != 1) {
    218                 cli->sendMsg(ResponseCode::CommandParameterError, "Invalid route prefix", false);
    219                 return 0;
    220             }
    221             if (!strcmp(argv[2], "add")) {
    222                 if (!strcmp(argv[4], "default")) {
    223                     if (ifc_add_route(argv[3], argv[5], prefix_length, argv[7])) {
    224                         cli->sendMsg(ResponseCode::OperationFailed,
    225                                 "Failed to add route to default table", true);
    226                     } else {
    227                         cli->sendMsg(ResponseCode::CommandOkay,
    228                                 "Route added to default table", false);
    229                     }
    230                 } else if (!strcmp(argv[4], "secondary")) {
    231                     return sSecondaryTableCtrl->addRoute(cli, argv[3], argv[5],
    232                             prefix_length, argv[7]);
    233                 } else {
    234                     cli->sendMsg(ResponseCode::CommandParameterError,
    235                             "Invalid route type, expecting 'default' or 'secondary'", false);
    236                     return 0;
    237                 }
    238             } else if (!strcmp(argv[2], "remove")) {
    239                 if (!strcmp(argv[4], "default")) {
    240                     if (ifc_remove_route(argv[3], argv[5], prefix_length, argv[7])) {
    241                         cli->sendMsg(ResponseCode::OperationFailed,
    242                                 "Failed to remove route from default table", true);
    243                     } else {
    244                         cli->sendMsg(ResponseCode::CommandOkay,
    245                                 "Route removed from default table", false);
    246                     }
    247                 } else if (!strcmp(argv[4], "secondary")) {
    248                     return sSecondaryTableCtrl->removeRoute(cli, argv[3], argv[5],
    249                             prefix_length, argv[7]);
    250                 } else {
    251                     cli->sendMsg(ResponseCode::CommandParameterError,
    252                             "Invalid route type, expecting 'default' or 'secondary'", false);
    253                     return 0;
    254                 }
    255             } else {
    256                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown interface cmd", false);
    257             }
    258             return 0;
    259         }
    260 
    261         if (!strcmp(argv[1], "getcfg")) {
    262             struct in_addr addr;
    263             int prefixLength;
    264             unsigned char hwaddr[6];
    265             unsigned flags = 0;
    266 
    267             ifc_init();
    268             memset(hwaddr, 0, sizeof(hwaddr));
    269 
    270             if (ifc_get_info(argv[2], &addr.s_addr, &prefixLength, &flags)) {
    271                 cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true);
    272                 ifc_close();
    273                 return 0;
    274             }
    275 
    276             if (ifc_get_hwaddr(argv[2], (void *) hwaddr)) {
    277                 LOGW("Failed to retrieve HW addr for %s (%s)", argv[2], strerror(errno));
    278             }
    279 
    280             char *addr_s = strdup(inet_ntoa(addr));
    281             const char *updown, *brdcst, *loopbk, *ppp, *running, *multi;
    282 
    283             updown =  (flags & IFF_UP)           ? "up" : "down";
    284             brdcst =  (flags & IFF_BROADCAST)    ? " broadcast" : "";
    285             loopbk =  (flags & IFF_LOOPBACK)     ? " loopback" : "";
    286             ppp =     (flags & IFF_POINTOPOINT)  ? " point-to-point" : "";
    287             running = (flags & IFF_RUNNING)      ? " running" : "";
    288             multi =   (flags & IFF_MULTICAST)    ? " multicast" : "";
    289 
    290             char *flag_s;
    291 
    292             asprintf(&flag_s, "[%s%s%s%s%s%s]", updown, brdcst, loopbk, ppp, running, multi);
    293 
    294             char *msg = NULL;
    295             asprintf(&msg, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %d %s",
    296                      hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5],
    297                      addr_s, prefixLength, flag_s);
    298 
    299             cli->sendMsg(ResponseCode::InterfaceGetCfgResult, msg, false);
    300 
    301             free(addr_s);
    302             free(flag_s);
    303             free(msg);
    304 
    305             ifc_close();
    306             return 0;
    307         } else if (!strcmp(argv[1], "setcfg")) {
    308             // arglist: iface addr prefixLength [flags]
    309             if (argc < 5) {
    310                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    311                 return 0;
    312             }
    313             LOGD("Setting iface cfg");
    314 
    315             struct in_addr addr;
    316             unsigned flags = 0;
    317 
    318             if (!inet_aton(argv[3], &addr)) {
    319                 cli->sendMsg(ResponseCode::CommandParameterError, "Invalid address", false);
    320                 return 0;
    321             }
    322 
    323             ifc_init();
    324             if (ifc_set_addr(argv[2], addr.s_addr)) {
    325                 cli->sendMsg(ResponseCode::OperationFailed, "Failed to set address", true);
    326                 ifc_close();
    327                 return 0;
    328             }
    329 
    330             //Set prefix length on a non zero address
    331             if (addr.s_addr != 0 && ifc_set_prefixLength(argv[2], atoi(argv[4]))) {
    332                 cli->sendMsg(ResponseCode::OperationFailed, "Failed to set prefixLength", true);
    333                 ifc_close();
    334                 return 0;
    335             }
    336 
    337             /* Process flags */
    338             /* read from "[XX" arg to "YY]" arg */
    339             bool bStarted = false;
    340             for (int i = 5; i < argc; i++) {
    341                 char *flag = argv[i];
    342                 if (!bStarted) {
    343                     if (*flag == '[') {
    344                         flag++;
    345                         bStarted = true;
    346                     } else {
    347                         continue;
    348                     }
    349                 }
    350                 int len = strlen(flag);
    351                 if (flag[len-1] == ']') {
    352                     i = argc;  // stop after this loop
    353                     flag[len-1] = 0;
    354                 }
    355                 if (!strcmp(flag, "up")) {
    356                     LOGD("Trying to bring up %s", argv[2]);
    357                     if (ifc_up(argv[2])) {
    358                         LOGE("Error upping interface");
    359                         cli->sendMsg(ResponseCode::OperationFailed, "Failed to up interface", true);
    360                         ifc_close();
    361                         return 0;
    362                     }
    363                 } else if (!strcmp(flag, "down")) {
    364                     LOGD("Trying to bring down %s", argv[2]);
    365                     if (ifc_down(argv[2])) {
    366                         LOGE("Error downing interface");
    367                         cli->sendMsg(ResponseCode::OperationFailed, "Failed to down interface", true);
    368                         ifc_close();
    369                         return 0;
    370                     }
    371                 } else if (!strcmp(flag, "broadcast")) {
    372                     LOGD("broadcast flag ignored");
    373                 } else if (!strcmp(flag, "multicast")) {
    374                     LOGD("multicast flag ignored");
    375                 } else {
    376                     cli->sendMsg(ResponseCode::CommandParameterError, "Flag unsupported", false);
    377                     ifc_close();
    378                     return 0;
    379                 }
    380             }
    381 
    382             cli->sendMsg(ResponseCode::CommandOkay, "Interface configuration set", false);
    383             ifc_close();
    384             return 0;
    385         } else if (!strcmp(argv[1], "clearaddrs")) {
    386             // arglist: iface
    387             LOGD("Clearing all IP addresses on %s", argv[2]);
    388 
    389             ifc_clear_addresses(argv[2]);
    390 
    391             cli->sendMsg(ResponseCode::CommandOkay, "Interface IP addresses cleared", false);
    392             return 0;
    393         } else if (!strcmp(argv[1], "ipv6privacyextensions")) {
    394             if (argc != 4) {
    395                 cli->sendMsg(ResponseCode::CommandSyntaxError,
    396                         "Usage: interface ipv6privacyextensions <interface> <enable|disable>",
    397                         false);
    398                 return 0;
    399             }
    400 
    401             char *tmp;
    402             asprintf(&tmp, "/proc/sys/net/ipv6/conf/%s/use_tempaddr", argv[2]);
    403 
    404             if (writeFile(tmp, !strncmp(argv[3], "enable", 7) ? "2" : "0", 1) < 0) {
    405                 free(tmp);
    406                 cli->sendMsg(ResponseCode::OperationFailed,
    407                         "Failed to set ipv6 privacy extensions", true);
    408                 return 0;
    409             }
    410 
    411             free(tmp);
    412             cli->sendMsg(ResponseCode::CommandOkay, "IPv6 privacy extensions changed", false);
    413             return 0;
    414         } else if (!strcmp(argv[1], "ipv6")) {
    415             if (argc != 4) {
    416                 cli->sendMsg(ResponseCode::CommandSyntaxError,
    417                         "Usage: interface ipv6 <interface> <enable|disable>",
    418                         false);
    419                 return 0;
    420             }
    421 
    422             char *tmp;
    423             asprintf(&tmp, "/proc/sys/net/ipv6/conf/%s/disable_ipv6", argv[2]);
    424 
    425             if (writeFile(tmp, !strncmp(argv[3], "enable", 7) ? "0" : "1", 1) < 0) {
    426                 free(tmp);
    427                 cli->sendMsg(ResponseCode::OperationFailed,
    428                         "Failed to change IPv6 state", true);
    429                 return 0;
    430             }
    431 
    432             free(tmp);
    433             cli->sendMsg(ResponseCode::CommandOkay, "IPv6 state changed", false);
    434             return 0;
    435         } else {
    436             cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown interface cmd", false);
    437             return 0;
    438         }
    439     }
    440     return 0;
    441 }
    442 
    443 
    444 CommandListener::ListTtysCmd::ListTtysCmd() :
    445                  NetdCommand("list_ttys") {
    446 }
    447 
    448 int CommandListener::ListTtysCmd::runCommand(SocketClient *cli,
    449                                              int argc, char **argv) {
    450     TtyCollection *tlist = sPppCtrl->getTtyList();
    451     TtyCollection::iterator it;
    452 
    453     for (it = tlist->begin(); it != tlist->end(); ++it) {
    454         cli->sendMsg(ResponseCode::TtyListResult, *it, false);
    455     }
    456 
    457     cli->sendMsg(ResponseCode::CommandOkay, "Ttys listed.", false);
    458     return 0;
    459 }
    460 
    461 CommandListener::IpFwdCmd::IpFwdCmd() :
    462                  NetdCommand("ipfwd") {
    463 }
    464 
    465 int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
    466                                                       int argc, char **argv) {
    467     int rc = 0;
    468 
    469     if (argc < 2) {
    470         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    471         return 0;
    472     }
    473 
    474     if (!strcmp(argv[1], "status")) {
    475         char *tmp = NULL;
    476 
    477         asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
    478         cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
    479         free(tmp);
    480         return 0;
    481     } else if (!strcmp(argv[1], "enable")) {
    482         rc = sTetherCtrl->setIpFwdEnabled(true);
    483     } else if (!strcmp(argv[1], "disable")) {
    484         rc = sTetherCtrl->setIpFwdEnabled(false);
    485     } else {
    486         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
    487         return 0;
    488     }
    489 
    490     if (!rc) {
    491         cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
    492     } else {
    493         cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
    494     }
    495 
    496     return 0;
    497 }
    498 
    499 CommandListener::TetherCmd::TetherCmd() :
    500                  NetdCommand("tether") {
    501 }
    502 
    503 int CommandListener::TetherCmd::runCommand(SocketClient *cli,
    504                                                       int argc, char **argv) {
    505     int rc = 0;
    506 
    507     if (argc < 2) {
    508         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    509         return 0;
    510     }
    511 
    512     if (!strcmp(argv[1], "stop")) {
    513         rc = sTetherCtrl->stopTethering();
    514     } else if (!strcmp(argv[1], "status")) {
    515         char *tmp = NULL;
    516 
    517         asprintf(&tmp, "Tethering services %s",
    518                  (sTetherCtrl->isTetheringStarted() ? "started" : "stopped"));
    519         cli->sendMsg(ResponseCode::TetherStatusResult, tmp, false);
    520         free(tmp);
    521         return 0;
    522     } else {
    523         /*
    524          * These commands take a minimum of 4 arguments
    525          */
    526         if (argc < 4) {
    527             cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    528             return 0;
    529         }
    530 
    531         if (!strcmp(argv[1], "start")) {
    532             if (argc % 2 == 1) {
    533                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Bad number of arguments", false);
    534                 return 0;
    535             }
    536 
    537             int num_addrs = argc - 2;
    538             int arg_index = 2;
    539             int array_index = 0;
    540             in_addr *addrs = (in_addr *)malloc(sizeof(in_addr) * num_addrs);
    541             while (array_index < num_addrs) {
    542                 if (!inet_aton(argv[arg_index++], &(addrs[array_index++]))) {
    543                     cli->sendMsg(ResponseCode::CommandParameterError, "Invalid address", false);
    544                     free(addrs);
    545                     return 0;
    546                 }
    547             }
    548             rc = sTetherCtrl->startTethering(num_addrs, addrs);
    549             free(addrs);
    550         } else if (!strcmp(argv[1], "interface")) {
    551             if (!strcmp(argv[2], "add")) {
    552                 rc = sTetherCtrl->tetherInterface(argv[3]);
    553             } else if (!strcmp(argv[2], "remove")) {
    554                 rc = sTetherCtrl->untetherInterface(argv[3]);
    555             } else if (!strcmp(argv[2], "list")) {
    556                 InterfaceCollection *ilist = sTetherCtrl->getTetheredInterfaceList();
    557                 InterfaceCollection::iterator it;
    558 
    559                 for (it = ilist->begin(); it != ilist->end(); ++it) {
    560                     cli->sendMsg(ResponseCode::TetherInterfaceListResult, *it, false);
    561                 }
    562             } else {
    563                 cli->sendMsg(ResponseCode::CommandParameterError,
    564                              "Unknown tether interface operation", false);
    565                 return 0;
    566             }
    567         } else if (!strcmp(argv[1], "dns")) {
    568             if (!strcmp(argv[2], "set")) {
    569                 rc = sTetherCtrl->setDnsForwarders(&argv[3], argc - 3);
    570             } else if (!strcmp(argv[2], "list")) {
    571                 NetAddressCollection *dlist = sTetherCtrl->getDnsForwarders();
    572                 NetAddressCollection::iterator it;
    573 
    574                 for (it = dlist->begin(); it != dlist->end(); ++it) {
    575                     cli->sendMsg(ResponseCode::TetherDnsFwdTgtListResult, inet_ntoa(*it), false);
    576                 }
    577             } else {
    578                 cli->sendMsg(ResponseCode::CommandParameterError,
    579                              "Unknown tether interface operation", false);
    580                 return 0;
    581             }
    582         } else {
    583             cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown tether cmd", false);
    584             return 0;
    585         }
    586     }
    587 
    588     if (!rc) {
    589         cli->sendMsg(ResponseCode::CommandOkay, "Tether operation succeeded", false);
    590     } else {
    591         cli->sendMsg(ResponseCode::OperationFailed, "Tether operation failed", true);
    592     }
    593 
    594     return 0;
    595 }
    596 
    597 CommandListener::NatCmd::NatCmd() :
    598                  NetdCommand("nat") {
    599 }
    600 
    601 int CommandListener::NatCmd::runCommand(SocketClient *cli,
    602                                                       int argc, char **argv) {
    603     int rc = 0;
    604 
    605     if (argc < 5) {
    606         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    607         return 0;
    608     }
    609 
    610     if (!strcmp(argv[1], "enable")) {
    611         rc = sNatCtrl->enableNat(argc, argv);
    612         if(!rc) {
    613             /* Ignore ifaces for now. */
    614             rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
    615         }
    616     } else if (!strcmp(argv[1], "disable")) {
    617         /* Ignore ifaces for now. */
    618         rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
    619         rc |= sNatCtrl->disableNat(argc, argv);
    620     } else {
    621         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown nat cmd", false);
    622         return 0;
    623     }
    624 
    625     if (!rc) {
    626         cli->sendMsg(ResponseCode::CommandOkay, "Nat operation succeeded", false);
    627     } else {
    628         cli->sendMsg(ResponseCode::OperationFailed, "Nat operation failed", true);
    629     }
    630 
    631     return 0;
    632 }
    633 
    634 CommandListener::PppdCmd::PppdCmd() :
    635                  NetdCommand("pppd") {
    636 }
    637 
    638 int CommandListener::PppdCmd::runCommand(SocketClient *cli,
    639                                                       int argc, char **argv) {
    640     int rc = 0;
    641 
    642     if (argc < 3) {
    643         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    644         return 0;
    645     }
    646 
    647     if (!strcmp(argv[1], "attach")) {
    648         struct in_addr l, r, dns1, dns2;
    649 
    650         memset(&dns1, sizeof(struct in_addr), 0);
    651         memset(&dns2, sizeof(struct in_addr), 0);
    652 
    653         if (!inet_aton(argv[3], &l)) {
    654             cli->sendMsg(ResponseCode::CommandParameterError, "Invalid local address", false);
    655             return 0;
    656         }
    657         if (!inet_aton(argv[4], &r)) {
    658             cli->sendMsg(ResponseCode::CommandParameterError, "Invalid remote address", false);
    659             return 0;
    660         }
    661         if ((argc > 3) && (!inet_aton(argv[5], &dns1))) {
    662             cli->sendMsg(ResponseCode::CommandParameterError, "Invalid dns1 address", false);
    663             return 0;
    664         }
    665         if ((argc > 4) && (!inet_aton(argv[6], &dns2))) {
    666             cli->sendMsg(ResponseCode::CommandParameterError, "Invalid dns2 address", false);
    667             return 0;
    668         }
    669         rc = sPppCtrl->attachPppd(argv[2], l, r, dns1, dns2);
    670     } else if (!strcmp(argv[1], "detach")) {
    671         rc = sPppCtrl->detachPppd(argv[2]);
    672     } else {
    673         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown pppd cmd", false);
    674         return 0;
    675     }
    676 
    677     if (!rc) {
    678         cli->sendMsg(ResponseCode::CommandOkay, "Pppd operation succeeded", false);
    679     } else {
    680         cli->sendMsg(ResponseCode::OperationFailed, "Pppd operation failed", true);
    681     }
    682 
    683     return 0;
    684 }
    685 
    686 CommandListener::PanCmd::PanCmd() :
    687                  NetdCommand("pan") {
    688 }
    689 
    690 int CommandListener::PanCmd::runCommand(SocketClient *cli,
    691                                         int argc, char **argv) {
    692     int rc = 0;
    693 
    694     if (argc < 2) {
    695         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
    696         return 0;
    697     }
    698 
    699     if (!strcmp(argv[1], "start")) {
    700         rc = sPanCtrl->startPan();
    701     } else if (!strcmp(argv[1], "stop")) {
    702         rc = sPanCtrl->stopPan();
    703     } else if (!strcmp(argv[1], "status")) {
    704         char *tmp = NULL;
    705 
    706         asprintf(&tmp, "Pan services %s",
    707                  (sPanCtrl->isPanStarted() ? "started" : "stopped"));
    708         cli->sendMsg(ResponseCode::PanStatusResult, tmp, false);
    709         free(tmp);
    710         return 0;
    711     } else {
    712         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown pan cmd", false);
    713         return 0;
    714     }
    715 
    716     if (!rc) {
    717         cli->sendMsg(ResponseCode::CommandOkay, "Pan operation succeeded", false);
    718     } else {
    719         cli->sendMsg(ResponseCode::OperationFailed, "Pan operation failed", true);
    720     }
    721 
    722     return 0;
    723 }
    724 
    725 CommandListener::SoftapCmd::SoftapCmd() :
    726                  NetdCommand("softap") {
    727 }
    728 
    729 int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
    730                                         int argc, char **argv) {
    731     int rc = 0, flag = 0;
    732     char *retbuf = NULL;
    733 
    734     if (argc < 2) {
    735         cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);
    736         return 0;
    737     }
    738 
    739     if (!strcmp(argv[1], "start")) {
    740         rc = sSoftapCtrl->startDriver(argv[2]);
    741     } else if (!strcmp(argv[1], "stop")) {
    742         rc = sSoftapCtrl->stopDriver(argv[2]);
    743     } else if (!strcmp(argv[1], "startap")) {
    744         rc = sSoftapCtrl->startSoftap();
    745     } else if (!strcmp(argv[1], "stopap")) {
    746         rc = sSoftapCtrl->stopSoftap();
    747     } else if (!strcmp(argv[1], "fwreload")) {
    748         rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
    749     } else if (!strcmp(argv[1], "clients")) {
    750         rc = sSoftapCtrl->clientsSoftap(&retbuf);
    751         if (!rc) {
    752             cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);
    753             free(retbuf);
    754             return 0;
    755         }
    756     } else if (!strcmp(argv[1], "status")) {
    757         asprintf(&retbuf, "Softap service %s",
    758                  (sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));
    759         cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);
    760         free(retbuf);
    761         return 0;
    762     } else if (!strcmp(argv[1], "set")) {
    763         rc = sSoftapCtrl->setSoftap(argc, argv);
    764     } else {
    765         cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd", false);
    766         return 0;
    767     }
    768 
    769     if (!rc) {
    770         cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
    771     } else {
    772         cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
    773     }
    774 
    775     return 0;
    776 }
    777 
    778 CommandListener::ResolverCmd::ResolverCmd() :
    779         NetdCommand("resolver") {
    780 }
    781 
    782 int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char **argv) {
    783     int rc = 0;
    784     struct in_addr addr;
    785 
    786     if (argc < 2) {
    787         cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
    788         return 0;
    789     }
    790 
    791     if (!strcmp(argv[1], "setdefaultif")) { // "resolver setdefaultif <iface>"
    792         if (argc == 3) {
    793             rc = sResolverCtrl->setDefaultInterface(argv[2]);
    794         } else {
    795             cli->sendMsg(ResponseCode::CommandSyntaxError,
    796                     "Wrong number of arguments to resolver setdefaultif", false);
    797             return 0;
    798         }
    799     } else if (!strcmp(argv[1], "setifdns")) { // "resolver setifdns <iface> <dns1> <dns2> ..."
    800         if (argc >= 4) {
    801             rc = sResolverCtrl->setInterfaceDnsServers(argv[2], &argv[3], argc - 3);
    802         } else {
    803             cli->sendMsg(ResponseCode::CommandSyntaxError,
    804                     "Wrong number of arguments to resolver setifdns", false);
    805             return 0;
    806         }
    807 
    808         // set the address of the interface to which the name servers
    809         // are bound. Required in order to bind to right interface when
    810         // doing the dns query.
    811         if (!rc) {
    812             ifc_init();
    813             ifc_get_info(argv[2], &addr.s_addr, NULL, 0);
    814 
    815             rc = sResolverCtrl->setInterfaceAddress(argv[2], &addr);
    816         }
    817     } else if (!strcmp(argv[1], "flushdefaultif")) { // "resolver flushdefaultif"
    818         if (argc == 2) {
    819             rc = sResolverCtrl->flushDefaultDnsCache();
    820         } else {
    821             cli->sendMsg(ResponseCode::CommandSyntaxError,
    822                     "Wrong number of arguments to resolver flushdefaultif", false);
    823             return 0;
    824         }
    825     } else if (!strcmp(argv[1], "flushif")) { // "resolver flushif <iface>"
    826         if (argc == 3) {
    827             rc = sResolverCtrl->flushInterfaceDnsCache(argv[2]);
    828         } else {
    829             cli->sendMsg(ResponseCode::CommandSyntaxError,
    830                     "Wrong number of arguments to resolver setdefaultif", false);
    831             return 0;
    832         }
    833     } else {
    834         cli->sendMsg(ResponseCode::CommandSyntaxError,"Resolver unknown command", false);
    835         return 0;
    836     }
    837 
    838     if (!rc) {
    839         cli->sendMsg(ResponseCode::CommandOkay, "Resolver command succeeded", false);
    840     } else {
    841         cli->sendMsg(ResponseCode::OperationFailed, "Resolver command failed", true);
    842     }
    843 
    844     return 0;
    845 }
    846 
    847 int CommandListener::readInterfaceCounters(const char *iface, unsigned long *rx, unsigned long *tx) {
    848     FILE *fp = fopen("/proc/net/dev", "r");
    849     if (!fp) {
    850         LOGE("Failed to open /proc/net/dev (%s)", strerror(errno));
    851         return -1;
    852     }
    853 
    854     char buffer[512];
    855 
    856     fgets(buffer, sizeof(buffer), fp); // Header 1
    857     fgets(buffer, sizeof(buffer), fp); // Header 2
    858     while(fgets(buffer, sizeof(buffer), fp)) {
    859         buffer[strlen(buffer)-1] = '\0';
    860 
    861         char name[31];
    862         unsigned long d;
    863         sscanf(buffer, "%30s %lu %lu %lu %lu %lu %lu %lu %lu %lu",
    864                 name, rx, &d, &d, &d, &d, &d, &d, &d, tx);
    865         char *rxString = strchr(name, ':');
    866         *rxString = '\0';
    867         rxString++;
    868         // when the rx count gets too big it changes from "name: 999" to "name:1000"
    869         // and the sscanf munge the two together.  Detect that and fix
    870         // note that all the %lu will be off by one and the real tx value will be in d
    871         if (*rxString != '\0') {
    872             *tx = d;
    873             sscanf(rxString, "%20lu", rx);
    874         }
    875         if (strcmp(name, iface)) {
    876             continue;
    877         }
    878         fclose(fp);
    879         return 0;
    880     }
    881 
    882     fclose(fp);
    883     *rx = 0;
    884     *tx = 0;
    885     return 0;
    886 }
    887 
    888 CommandListener::BandwidthControlCmd::BandwidthControlCmd() :
    889     NetdCommand("bandwidth") {
    890 }
    891 
    892 void CommandListener::BandwidthControlCmd::sendGenericSyntaxError(SocketClient *cli, const char *usageMsg) {
    893     char *msg;
    894     asprintf(&msg, "Usage: bandwidth %s", usageMsg);
    895     cli->sendMsg(ResponseCode::CommandSyntaxError, msg, false);
    896     free(msg);
    897 }
    898 
    899 void CommandListener::BandwidthControlCmd::sendGenericOkFail(SocketClient *cli, int cond) {
    900     if (!cond) {
    901         cli->sendMsg(ResponseCode::CommandOkay, "Bandwidth command succeeeded", false);
    902     } else {
    903         cli->sendMsg(ResponseCode::OperationFailed, "Bandwidth command failed", false);
    904     }
    905 }
    906 
    907 void CommandListener::BandwidthControlCmd::sendGenericOpFailed(SocketClient *cli, const char *errMsg) {
    908     cli->sendMsg(ResponseCode::OperationFailed, errMsg, false);
    909 }
    910 
    911 int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc, char **argv) {
    912     if (argc < 2) {
    913         sendGenericSyntaxError(cli, "<cmds> <args...>");
    914         return 0;
    915     }
    916 
    917     LOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
    918 
    919     if (!strcmp(argv[1], "enable")) {
    920         int rc = sBandwidthCtrl->enableBandwidthControl();
    921         sendGenericOkFail(cli, rc);
    922         return 0;
    923 
    924     }
    925     if (!strcmp(argv[1], "disable")) {
    926         int rc = sBandwidthCtrl->disableBandwidthControl();
    927         sendGenericOkFail(cli, rc);
    928         return 0;
    929 
    930     }
    931     if (!strcmp(argv[1], "removequota") || !strcmp(argv[1], "rq")) {
    932         if (argc != 3) {
    933             sendGenericSyntaxError(cli, "removequota <interface>");
    934             return 0;
    935         }
    936         int rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[2]);
    937         sendGenericOkFail(cli, rc);
    938         return 0;
    939 
    940     }
    941     if (!strcmp(argv[1], "getquota") || !strcmp(argv[1], "gq")) {
    942         int64_t bytes;
    943         if (argc != 2) {
    944             sendGenericSyntaxError(cli, "getquota");
    945             return 0;
    946         }
    947         int rc = sBandwidthCtrl->getInterfaceSharedQuota(&bytes);
    948         if (rc) {
    949             sendGenericOpFailed(cli, "Failed to get quota");
    950             return 0;
    951         }
    952 
    953         char *msg;
    954         asprintf(&msg, "%lld", bytes);
    955         cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false);
    956         free(msg);
    957         return 0;
    958 
    959     }
    960     if (!strcmp(argv[1], "getiquota") || !strcmp(argv[1], "giq")) {
    961         int64_t bytes;
    962         if (argc != 3) {
    963             sendGenericSyntaxError(cli, "getiquota <iface>");
    964             return 0;
    965         }
    966 
    967         int rc = sBandwidthCtrl->getInterfaceQuota(argv[2], &bytes);
    968         if (rc) {
    969             sendGenericOpFailed(cli, "Failed to get quota");
    970             return 0;
    971         }
    972         char *msg;
    973         asprintf(&msg, "%lld", bytes);
    974         cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false);
    975         free(msg);
    976         return 0;
    977 
    978     }
    979     if (!strcmp(argv[1], "setquota") || !strcmp(argv[1], "sq")) {
    980         if (argc != 4) {
    981             sendGenericSyntaxError(cli, "setquota <interface> <bytes>");
    982             return 0;
    983         }
    984         int rc = sBandwidthCtrl->setInterfaceSharedQuota(argv[2], atoll(argv[3]));
    985         sendGenericOkFail(cli, rc);
    986         return 0;
    987     }
    988     if (!strcmp(argv[1], "setquotas") || !strcmp(argv[1], "sqs")) {
    989         int rc;
    990         if (argc < 4) {
    991             sendGenericSyntaxError(cli, "setquotas <bytes> <interface> ...");
    992             return 0;
    993         }
    994 
    995         for (int q = 3; argc >= 4; q++, argc--) {
    996             rc = sBandwidthCtrl->setInterfaceSharedQuota(argv[q], atoll(argv[2]));
    997             if (rc) {
    998                 char *msg;
    999                 asprintf(&msg, "bandwidth setquotas %s %s failed", argv[2], argv[q]);
   1000                 cli->sendMsg(ResponseCode::OperationFailed,
   1001                              msg, false);
   1002                 free(msg);
   1003                 return 0;
   1004             }
   1005         }
   1006         sendGenericOkFail(cli, rc);
   1007         return 0;
   1008 
   1009     }
   1010     if (!strcmp(argv[1], "removequotas") || !strcmp(argv[1], "rqs")) {
   1011         int rc;
   1012         if (argc < 3) {
   1013             sendGenericSyntaxError(cli, "removequotas <interface> ...");
   1014             return 0;
   1015         }
   1016 
   1017         for (int q = 2; argc >= 3; q++, argc--) {
   1018             rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[q]);
   1019             if (rc) {
   1020                 char *msg;
   1021                 asprintf(&msg, "bandwidth removequotas %s failed", argv[q]);
   1022                 cli->sendMsg(ResponseCode::OperationFailed,
   1023                              msg, false);
   1024                 free(msg);
   1025                 return 0;
   1026             }
   1027         }
   1028         sendGenericOkFail(cli, rc);
   1029         return 0;
   1030 
   1031     }
   1032     if (!strcmp(argv[1], "removeiquota") || !strcmp(argv[1], "riq")) {
   1033         if (argc != 3) {
   1034             sendGenericSyntaxError(cli, "removeiquota <interface>");
   1035             return 0;
   1036         }
   1037         int rc = sBandwidthCtrl->removeInterfaceQuota(argv[2]);
   1038         sendGenericOkFail(cli, rc);
   1039         return 0;
   1040 
   1041     }
   1042     if (!strcmp(argv[1], "setiquota") || !strcmp(argv[1], "siq")) {
   1043         if (argc != 4) {
   1044             sendGenericSyntaxError(cli, "setiquota <interface> <bytes>");
   1045             return 0;
   1046         }
   1047         int rc = sBandwidthCtrl->setInterfaceQuota(argv[2], atoll(argv[3]));
   1048         sendGenericOkFail(cli, rc);
   1049         return 0;
   1050 
   1051     }
   1052     if (!strcmp(argv[1], "addnaughtyapps") || !strcmp(argv[1], "ana")) {
   1053         if (argc < 3) {
   1054             sendGenericSyntaxError(cli, "addnaughtyapps <appUid> ...");
   1055             return 0;
   1056         }
   1057         int rc = sBandwidthCtrl->addNaughtyApps(argc - 2, argv + 2);
   1058         sendGenericOkFail(cli, rc);
   1059         return 0;
   1060 
   1061 
   1062     }
   1063     if (!strcmp(argv[1], "removenaughtyapps") || !strcmp(argv[1], "rna")) {
   1064         if (argc < 3) {
   1065             sendGenericSyntaxError(cli, "removenaughtyapps <appUid> ...");
   1066             return 0;
   1067         }
   1068         int rc = sBandwidthCtrl->removeNaughtyApps(argc - 2, argv + 2);
   1069         sendGenericOkFail(cli, rc);
   1070         return 0;
   1071 
   1072     }
   1073     if (!strcmp(argv[1], "setglobalalert") || !strcmp(argv[1], "sga")) {
   1074         if (argc != 3) {
   1075             sendGenericSyntaxError(cli, "setglobalalert <bytes>");
   1076             return 0;
   1077         }
   1078         int rc = sBandwidthCtrl->setGlobalAlert(atoll(argv[2]));
   1079         sendGenericOkFail(cli, rc);
   1080         return 0;
   1081 
   1082     }
   1083     if (!strcmp(argv[1], "debugsettetherglobalalert") || !strcmp(argv[1], "dstga")) {
   1084         if (argc != 4) {
   1085             sendGenericSyntaxError(cli, "debugsettetherglobalalert <interface0> <interface1>");
   1086             return 0;
   1087         }
   1088         /* We ignore the interfaces for now. */
   1089         int rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
   1090         sendGenericOkFail(cli, rc);
   1091         return 0;
   1092 
   1093     }
   1094     if (!strcmp(argv[1], "removeglobalalert") || !strcmp(argv[1], "rga")) {
   1095         if (argc != 2) {
   1096             sendGenericSyntaxError(cli, "removeglobalalert");
   1097             return 0;
   1098         }
   1099         int rc = sBandwidthCtrl->removeGlobalAlert();
   1100         sendGenericOkFail(cli, rc);
   1101         return 0;
   1102 
   1103     }
   1104     if (!strcmp(argv[1], "debugremovetetherglobalalert") || !strcmp(argv[1], "drtga")) {
   1105         if (argc != 4) {
   1106             sendGenericSyntaxError(cli, "debugremovetetherglobalalert <interface0> <interface1>");
   1107             return 0;
   1108         }
   1109         /* We ignore the interfaces for now. */
   1110         int rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
   1111         sendGenericOkFail(cli, rc);
   1112         return 0;
   1113 
   1114     }
   1115     if (!strcmp(argv[1], "setsharedalert") || !strcmp(argv[1], "ssa")) {
   1116         if (argc != 3) {
   1117             sendGenericSyntaxError(cli, "setsharedalert <bytes>");
   1118             return 0;
   1119         }
   1120         int rc = sBandwidthCtrl->setSharedAlert(atoll(argv[2]));
   1121         sendGenericOkFail(cli, rc);
   1122         return 0;
   1123 
   1124     }
   1125     if (!strcmp(argv[1], "removesharedalert") || !strcmp(argv[1], "rsa")) {
   1126         if (argc != 2) {
   1127             sendGenericSyntaxError(cli, "removesharedalert");
   1128             return 0;
   1129         }
   1130         int rc = sBandwidthCtrl->removeSharedAlert();
   1131         sendGenericOkFail(cli, rc);
   1132         return 0;
   1133 
   1134     }
   1135     if (!strcmp(argv[1], "setinterfacealert") || !strcmp(argv[1], "sia")) {
   1136         if (argc != 4) {
   1137             sendGenericSyntaxError(cli, "setinterfacealert <interface> <bytes>");
   1138             return 0;
   1139         }
   1140         int rc = sBandwidthCtrl->setInterfaceAlert(argv[2], atoll(argv[3]));
   1141         sendGenericOkFail(cli, rc);
   1142         return 0;
   1143 
   1144     }
   1145     if (!strcmp(argv[1], "removeinterfacealert") || !strcmp(argv[1], "ria")) {
   1146         if (argc != 3) {
   1147             sendGenericSyntaxError(cli, "removeinterfacealert <interface>");
   1148             return 0;
   1149         }
   1150         int rc = sBandwidthCtrl->removeInterfaceAlert(argv[2]);
   1151         sendGenericOkFail(cli, rc);
   1152         return 0;
   1153 
   1154     }
   1155     if (!strcmp(argv[1], "gettetherstats") || !strcmp(argv[1], "gts")) {
   1156         BandwidthController::TetherStats tetherStats;
   1157         if (argc != 4) {
   1158             sendGenericSyntaxError(cli, "gettetherstats <interface0> <interface1>");
   1159             return 0;
   1160         }
   1161 
   1162         tetherStats.ifaceIn = argv[2];
   1163         tetherStats.ifaceOut = argv[3];
   1164         int rc = sBandwidthCtrl->getTetherStats(tetherStats);
   1165         if (rc) {
   1166             sendGenericOpFailed(cli, "Failed to get tethering stats");
   1167             return 0;
   1168         }
   1169 
   1170         char *msg = tetherStats.getStatsLine();
   1171         cli->sendMsg(ResponseCode::TetheringStatsResult, msg, false);
   1172         free(msg);
   1173         return 0;
   1174 
   1175     }
   1176 
   1177     cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown bandwidth cmd", false);
   1178     return 0;
   1179 }
   1180