Ashlesh Gawande | 6c86e30 | 2019-09-17 22:27:05 -0500 | [diff] [blame] | 1 | # -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ |
| 2 | # |
| 3 | # Copyright (C) 2015-2019, The University of Memphis, |
| 4 | # Arizona Board of Regents, |
| 5 | # Regents of the University of California. |
| 6 | # |
| 7 | # This file is part of Mini-NDN. |
| 8 | # See AUTHORS.md for a complete list of Mini-NDN authors and contributors. |
| 9 | # |
| 10 | # Mini-NDN is free software: you can redistribute it and/or modify |
| 11 | # it under the terms of the GNU General Public License as published by |
| 12 | # the Free Software Foundation, either version 3 of the License, or |
| 13 | # (at your option) any later version. |
| 14 | # |
| 15 | # Mini-NDN is distributed in the hope that it will be useful, |
| 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | # GNU General Public License for more details. |
| 19 | # |
| 20 | # You should have received a copy of the GNU General Public License |
| 21 | # along with Mini-NDN, e.g., in COPYING.md file. |
| 22 | # If not, see <http://www.gnu.org/licenses/>. |
| 23 | |
| 24 | import shutil |
| 25 | import os, sys |
| 26 | |
| 27 | from mininet.clean import sh |
| 28 | from mininet.examples.cluster import RemoteMixin |
| 29 | from mininet.log import warn |
| 30 | from mininet.node import Switch |
| 31 | |
| 32 | from minindn.apps.application import Application |
| 33 | from minindn.util import scp, copyExistentFile |
| 34 | from minindn.helpers.nfdc import Nfdc |
| 35 | from minindn.minindn import Minindn |
| 36 | |
| 37 | class Nlsr(Application): |
| 38 | ROUTING_LINK_STATE = 'link-state' |
| 39 | ROUTING_HYPERBOLIC = 'hr' |
| 40 | ROUTING_DRY_RUN = 'dry' |
| 41 | SYNC_PSYNC = 'psync' |
| 42 | SYNC_CHRONOSYNC = 'chronosync' |
| 43 | |
| 44 | def __init__(self, node, logLevel='NONE', security=False, sync=SYNC_PSYNC, |
| 45 | faceType='udp', nFaces=3, routingType=ROUTING_LINK_STATE): |
| 46 | Application.__init__(self, node) |
| 47 | |
| 48 | self.network = '/ndn/' |
| 49 | self.node = node |
| 50 | self.parameters = self.node.params['params'] |
| 51 | |
| 52 | if self.parameters.get('nlsr-log-level', None) != None: |
| 53 | logLevel = self.parameters.get('nlsr-log-level') |
| 54 | |
| 55 | if logLevel in ['NONE', 'WARN', 'INFO', 'DEBUG', 'TRACE']: |
| 56 | self.envDict = {'NDN_LOG': 'nlsr.*={}'.format(logLevel)} |
| 57 | else: |
| 58 | self.envDict = {'NDN_LOG': logLevel} |
| 59 | |
| 60 | self.logFile = 'nlsr.log' |
| 61 | self.routerName = '/{}C1.Router/cs/{}'.format('%', node.name) |
| 62 | self.confFile = '{}/nlsr.conf'.format(self.homeDir) |
| 63 | self.security = security |
| 64 | self.sync = sync |
| 65 | self.faceType = faceType |
| 66 | self.infocmd = 'infoedit -f nlsr.conf' |
| 67 | |
| 68 | self.parameters = self.node.params['params'] |
| 69 | |
| 70 | self.nFaces = nFaces |
| 71 | if routingType == Nlsr.ROUTING_HYPERBOLIC: |
| 72 | self.hyperbolicState = 'on' |
| 73 | elif routingType == Nlsr.ROUTING_DRY_RUN: |
| 74 | self.hyperbolicState = 'dry-run' |
| 75 | else: |
| 76 | self.hyperbolicState = 'off' |
| 77 | self.hyperRadius = self.parameters.get('radius', 0.0) |
| 78 | self.hyperAngle = self.parameters.get('angle', 0.0) |
| 79 | |
| 80 | if ((self.hyperbolicState == 'on' or self.hyperbolicState == 'dry-run') and |
| 81 | (self.hyperRadius == 0.0 or self.hyperAngle == 0.0)): |
| 82 | warn('Hyperbolic coordinates in topology file are either missing or misconfigured.') |
| 83 | warn('Check that each node has one radius value and one or two angle value(s).') |
| 84 | sys.exit(1) |
| 85 | |
| 86 | self.neighborIPs = [] |
| 87 | possibleConfPaths = ['/usr/local/etc/ndn/nlsr.conf.sample', '/etc/ndn/nlsr.conf.sample'] |
| 88 | copyExistentFile(node, possibleConfPaths, '{}/nlsr.conf'.format(self.homeDir)) |
| 89 | |
| 90 | self.createConfigFile() |
| 91 | |
| 92 | if security and not Minindn.ndnSecurityDisabled: |
| 93 | self.createKeysAndCertificates() |
| 94 | |
| 95 | def start(self): |
| 96 | self.createFaces() |
| 97 | Application.start(self, 'nlsr -f {}'.format(self.confFile), self.logFile, self.envDict) |
| 98 | Minindn.sleep(1) |
| 99 | |
| 100 | def createFaces(self): |
| 101 | for ip in self.neighborIPs: |
| 102 | Nfdc.createFace(self.node, ip, self.faceType, isPermanent=True) |
| 103 | |
| 104 | @staticmethod |
| 105 | def createKey(host, name, outputFile): |
| 106 | host.cmd('ndnsec-keygen {} > {}'.format(name, outputFile)) |
| 107 | |
| 108 | @staticmethod |
| 109 | def createCertificate(host, signer, keyFile, outputFile): |
| 110 | host.cmd('ndnsec-certgen -s {} -r {} > {}'.format(signer, keyFile, outputFile)) |
| 111 | |
| 112 | def createKeysAndCertificates(self): |
Italo Valcy | ccd85b1 | 2020-07-24 12:35:20 -0500 | [diff] [blame] | 113 | securityDir = '{}/security'.format(Minindn.workDir) |
Ashlesh Gawande | 6c86e30 | 2019-09-17 22:27:05 -0500 | [diff] [blame] | 114 | |
| 115 | if not os.path.exists(securityDir): |
| 116 | os.mkdir(securityDir) |
| 117 | |
| 118 | rootName = self.network |
| 119 | rootCertFile = '{}/root.cert'.format(securityDir) |
| 120 | if not os.path.isfile(rootCertFile): |
| 121 | # Create root certificate |
| 122 | sh('ndnsec-keygen {}'.format(rootName)) # Installs a self-signed cert into the system |
| 123 | sh('ndnsec-cert-dump -i {} > {}'.format(rootName, rootCertFile)) |
| 124 | |
| 125 | # Create necessary certificates for each site |
| 126 | nodeSecurityFolder = '{}/security'.format(self.homeDir) |
| 127 | |
| 128 | self.node.cmd('mkdir -p {}'.format(nodeSecurityFolder)) |
| 129 | |
| 130 | # Create temp folders for remote nodes on this machine (localhost) to store site.key file |
| 131 | # from RemoteNodes |
| 132 | if not os.path.exists(nodeSecurityFolder) and \ |
| 133 | isinstance(self.node, RemoteMixin) and self.node.isRemote: |
| 134 | os.makedirs(nodeSecurityFolder) |
| 135 | |
| 136 | shutil.copyfile('{}/root.cert'.format(securityDir), |
| 137 | '{}/root.cert'.format(nodeSecurityFolder)) |
| 138 | |
| 139 | # Create site certificate |
| 140 | siteName = '{}{}-site'.format(self.network, self.node.name) |
| 141 | siteKeyFile = '{}/site.keys'.format(nodeSecurityFolder) |
| 142 | siteCertFile = '{}/site.cert'.format(nodeSecurityFolder) |
| 143 | Nlsr.createKey(self.node, siteName, siteKeyFile) |
| 144 | |
| 145 | # Copy siteKeyFile from remote for ndnsec-certgen |
| 146 | if isinstance(self.node, RemoteMixin) and self.node.isRemote: |
| 147 | login = 'mininet@{}'.format(self.node.server) |
| 148 | src = '{}:{}'.format(login, siteKeyFile) |
| 149 | dst = siteKeyFile |
| 150 | scp(src, dst) |
| 151 | |
| 152 | # Root key is in root namespace, must sign site key and then install on host |
| 153 | sh('ndnsec-certgen -s {} -r {} > {}'.format(rootName, siteKeyFile, siteCertFile)) |
| 154 | |
| 155 | # Copy root.cert and site.cert from localhost to remote host |
| 156 | if isinstance(self.node, RemoteMixin) and self.node.isRemote: |
| 157 | login = 'mininet@{}'.format(self.node.server) |
| 158 | src = '{}/site.cert'.format(nodeSecurityFolder) |
| 159 | src2 = '{}/root.cert'.format(nodeSecurityFolder) |
| 160 | dst = '{}:/tmp/'.format(login) |
| 161 | scp(src, src2, dst) |
| 162 | self.node.cmd('mv /tmp/*.cert {}'.format(nodeSecurityFolder)) |
| 163 | |
| 164 | self.node.cmd('ndnsec-cert-install -f {}'.format(siteCertFile)) |
| 165 | |
| 166 | # Create and install operator certificate |
| 167 | opName = '{}/%C1.Operator/op'.format(siteName) |
| 168 | opKeyFile = '{}/op.keys'.format(nodeSecurityFolder) |
| 169 | opCertFile = '{}/op.cert'.format(nodeSecurityFolder) |
| 170 | Nlsr.createKey(self.node, opName, opKeyFile) |
| 171 | Nlsr.createCertificate(self.node, siteName, opKeyFile, opCertFile) |
| 172 | self.node.cmd('ndnsec-cert-install -f {}'.format(opCertFile)) |
| 173 | |
| 174 | # Create and install router certificate |
| 175 | routerName = '{}/%C1.Router/cs/{}'.format(siteName, self.node.name) |
| 176 | routerKeyFile = '{}/router.keys'.format(nodeSecurityFolder) |
| 177 | routerCertFile = '{}/router.cert'.format(nodeSecurityFolder) |
| 178 | Nlsr.createKey(self.node, routerName, routerKeyFile) |
| 179 | Nlsr.createCertificate(self.node, opName, routerKeyFile, routerCertFile) |
| 180 | self.node.cmd('ndnsec-cert-install -f {}'.format(routerCertFile)) |
| 181 | |
| 182 | def createConfigFile(self): |
| 183 | |
| 184 | self.__editGeneralSection() |
| 185 | self.__editNeighborsSection() |
| 186 | self.__editHyperbolicSection() |
| 187 | self.__editFibSection() |
| 188 | self.__editAdvertisingSection() |
| 189 | self.__editSecuritySection() |
| 190 | |
| 191 | def __editGeneralSection(self): |
| 192 | |
| 193 | self.node.cmd('{} -s general.network -v {}'.format(self.infocmd, self.network)) |
| 194 | self.node.cmd('{} -s general.site -v /{}-site'.format(self.infocmd, self.node.name)) |
| 195 | self.node.cmd('{} -s general.router -v /%C1.Router/cs/{}'.format(self.infocmd, self.node.name)) |
| 196 | self.node.cmd('{} -s general.state-dir -v {}/log'.format(self.infocmd, self.homeDir)) |
| 197 | self.node.cmd('{} -s general.sync-protocol -v {}'.format(self.infocmd, self.sync)) |
| 198 | |
| 199 | def __editNeighborsSection(self): |
| 200 | |
| 201 | self.node.cmd('{} -d neighbors.neighbor'.format(self.infocmd)) |
| 202 | for intf in self.node.intfList(): |
| 203 | link = intf.link |
| 204 | if not link: |
| 205 | continue |
| 206 | |
| 207 | node1, node2 = link.intf1.node, link.intf2.node |
| 208 | |
| 209 | # Todo: add some switch support |
| 210 | if isinstance(node1, Switch) or isinstance(node2, Switch): |
| 211 | continue |
| 212 | |
| 213 | if node1 == self.node: |
| 214 | other = node2 |
| 215 | ip = other.IP(str(link.intf2)) |
| 216 | else: |
| 217 | other = node1 |
| 218 | ip = other.IP(str(link.intf1)) |
| 219 | |
| 220 | linkCost = intf.params.get('delay', '10ms').replace('ms', '') |
| 221 | |
| 222 | self.neighborIPs.append(ip) |
| 223 | |
| 224 | self.node.cmd('{} -a neighbors.neighbor \ |
| 225 | <<<\'name {}{}-site/%C1.Router/cs/{} face-uri {}://{}\n link-cost {}\'' |
| 226 | .format(self.infocmd, self.network, other.name, other.name, |
| 227 | self.faceType, ip, linkCost)) |
| 228 | |
| 229 | def __editHyperbolicSection(self): |
| 230 | |
| 231 | self.node.cmd('{} -s hyperbolic.state -v {}'.format(self.infocmd, self.hyperbolicState)) |
| 232 | self.node.cmd('{} -s hyperbolic.radius -v {}'.format(self.infocmd, self.hyperRadius)) |
| 233 | self.node.cmd('{} -s hyperbolic.angle -v {}'.format(self.infocmd, self.hyperAngle)) |
| 234 | |
| 235 | def __editFibSection(self): |
| 236 | |
| 237 | self.node.cmd('{} -s fib.max-faces-per-prefix -v {}'.format(self.infocmd, self.nFaces)) |
| 238 | |
| 239 | def __editAdvertisingSection(self): |
| 240 | |
| 241 | self.node.cmd('{} -d advertising.prefix'.format(self.infocmd)) |
| 242 | self.node.cmd('{} -s advertising.prefix -v {}{}-site/{}' |
| 243 | .format(self.infocmd, self.network, self.node.name, self.node.name)) |
| 244 | |
| 245 | def __editSecuritySection(self): |
| 246 | |
| 247 | self.node.cmd('{} -d security.cert-to-publish'.format(self.infocmd)) |
| 248 | if not self.security: |
| 249 | self.node.cmd('{} -s security.validator.trust-anchor.type -v any'.format(self.infocmd)) |
| 250 | self.node.cmd('{} -d security.validator.trust-anchor.file-name'.format(self.infocmd)) |
| 251 | self.node.cmd('{} -s security.prefix-update-validator.trust-anchor.type -v any'.format(self.infocmd)) |
| 252 | self.node.cmd('{} -d security.prefix-update-validator.trust-anchor.file-name'.format(self.infocmd)) |
| 253 | else: |
| 254 | self.node.cmd('{} -s security.validator.trust-anchor.file-name -v security/root.cert'.format(self.infocmd)) |
| 255 | self.node.cmd('{} -s security.prefix-update-validator.trust-anchor.file-name -v security/site.cert'.format(self.infocmd)) |
| 256 | self.node.cmd('{} -p security.cert-to-publish -v security/site.cert'.format(self.infocmd)) |
| 257 | self.node.cmd('{} -p security.cert-to-publish -v security/op.cert'.format(self.infocmd)) |
| 258 | self.node.cmd('{} -p security.cert-to-publish -v security/router.cert'.format(self.infocmd)) |