README 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. # This file contains some examples of using NCD, the Network Configuration Daemon.
  2. #
  3. # A short introduction to NCD follows.
  4. #
  5. # NCD is a general-purpose system configuration system, operated with a unique programming language.
  6. # The configuration consists of one or more so-called processes that can be considered executing in
  7. # parallel. Further, each process consists of one or more statements, representing the individual
  8. # actions. Statements are implemented as modules built into NCD.
  9. #
  10. # Inside a process, statements can be considered "executed" one after another. That is, when NCD
  11. # starts up, it initializes the first statement, putting it in the DOWN state. When the statement
  12. # reports having transitioned into the UP state, it initializes the next statement in the DOWN state,
  13. # and so on.
  14. #
  15. # However, execution can go in the other direction too. A statement in the UP state can, at any time,
  16. # report having transitioned into the DOWN state. At this point, any statements after that one will
  17. # automatically be de-initialized. The de-initiazation is done from the bottom up. First the last
  18. # initialized statement after the problematic statement is requested to terminate and enters the
  19. # DYING state. After it terminates, its preceding statement enters the DYING state, and so on, until
  20. # all statements following the problematic statement have been de-initiazed.
  21. #
  22. # The backward-execution is the key feature of NCD, and is particularly well suited for programming
  23. # system configurations. Read on to see why.
  24. #
  25. # Statements in NCD can be divided into two categories:
  26. # - Statements that configure something. These statements transition into the UP state "immediately".
  27. # On de-initialization, such statements perform the reverse operation of what they did when initialized.
  28. # Imaginary example: a statement that turn a light on intialization, and turns if off on de-initialization.
  29. # - Statements that wait for something. These statements may remain in the DOWN state indefinitely.
  30. # They enter the UP state when the waited-for condition is satisfied, and also go back into the DOWN
  31. # state when it is no longer satisfied.
  32. # Imaginary example: a statement that is UP when a switch is turned on, and DOWN when it is turned off.
  33. #
  34. # Using the two example statements, we can constuct a process that controls the light based on the switch:
  35. # (these are not really implemented in NCD :)
  36. #
  37. # process light {
  38. # wait_switch();
  39. # turn_light();
  40. # }
  41. #
  42. # When the switch is turned on, wait_switch() will transition to UP, initializing turn_light(), turning the
  43. # light on. When the switch is turned off, wait_switch() will transition to DOWN, causing the de-initialization
  44. # of turn_light(), turning the light off.
  45. # We can add another turn_light() at the end to make the switch control two lights.
  46. #
  47. # A more complex example: We have a christmas three with lights on it. There are multiple "regular" lights,
  48. # controlled with switches, and a special "top" light. The regular lights take a long time to turn on, and
  49. # each takes a different, unpredictable time. We want the top light to be turned on if and only if all the regular
  50. # lights are completely on.
  51. #
  52. # This problem can easily be solved using dependencies. NCD has built-in support for dependencies, provided
  53. # in the form of provide() and depend() statements. A depend() statement is DOWN when its corresponding
  54. # provide() statement is not initialized, and UP when it is. When a provide() is requested to de-initialize, it
  55. # transitions the depend() statements back into the DOWN state, and, before actually dying, waits for any
  56. # statements following them to de-initialize.
  57. #
  58. # The christmas three problem can then be programmed as follows:
  59. #
  60. # process light1 {
  61. # wait_switch1();
  62. # turn_light1();
  63. # provide("L1");
  64. # }
  65. #
  66. # process light2 {
  67. # wait_switch2();
  68. # turn_light2();
  69. # provide("L2");
  70. # }
  71. #
  72. # process top_light {
  73. # depend("L1");
  74. # depend("L2");
  75. # turn_top_light();
  76. # }
  77. #
  78. # Follow some real examples of network configuration using NCD.
  79. # For a list of implemented statements and their descriptions, take a look at the BadVPN source code, in
  80. # the ncd/modules/ folder.
  81. #
  82. #
  83. # Network card using DHCP.
  84. #
  85. process lan {
  86. # Make the interface name a variable so we can refer to it.
  87. # The NCD language has no notion of assigning a variable. Instead variables are
  88. # provided by statements preceding the statement where they are used.
  89. # The built-in var() statement can be used to make an alias.
  90. var("eth0") dev;
  91. # Wait for the network card to appear, set it up and wait for the cable to be
  92. # plugged it.
  93. net.backend.waitdevice(dev);
  94. net.up(dev);
  95. net.backend.waitlink(dev);
  96. # Start DHCP.
  97. net.ipv4.dhcp(dev) dhcp;
  98. # DHCP has obtained an address.
  99. # Because net.ipv4.dhcp does no checks of the IP address, as a safety measure, do not proceed
  100. # if the address is local.
  101. ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local;
  102. ifnot(test_local);
  103. # Assign the obtained address to the interface.
  104. net.ipv4.addr(dev, dhcp.addr, dhcp.prefix);
  105. # Add a default route.
  106. # <dest> <dest_prefix> <gateway/"none"> <metric> <device>
  107. net.ipv4.route("0.0.0.0", "0", dhcp.gateway, "20", dev);
  108. # Add DNS servers, as provided by DHCP.
  109. # "20" is the priority of the servers. When applying DNS servers, NCD collects the servers
  110. # from all active net.dns() statements, sorts them by priority ascending (stable), and writes
  111. # them to /etc/resolv.conf, overwriting anything that was previously there.
  112. net.dns(dhcp.dns_servers, "20");
  113. }
  114. #
  115. # Network card with static configuration.
  116. #
  117. process lan2 {
  118. # Make the interface name a variable so we can refer to it.
  119. var("eth1") dev;
  120. # Wait for the network card to appear, set it up and wait for the cable to be
  121. # plugged it.
  122. net.backend.waitdevice(dev);
  123. net.up(dev);
  124. net.backend.waitlink(dev);
  125. # Assign an IP address.
  126. # "24" is prefix length, i.e. subnet mask 255.255.255.0
  127. net.ipv4.addr(dev, "192.168.62.3", "24");
  128. # Add a default route.
  129. net.ipv4.route("0.0.0.0", "0", "192.168.62.3", "20", dev);
  130. # Build a list of DNS servers.
  131. # The NCD language does not support "expressions" - statement arguments must be
  132. # constant strings or variables referring to preceding statements.
  133. # A list can be constructed using the built-in list() statement.
  134. list("192.168.62.5", "192.168.62.6") dns_servers;
  135. # Add the DNS servers.
  136. net.dns(dns_servers, "20");
  137. }
  138. #
  139. # Wireless network interface using wpa_supplicant.
  140. #
  141. process WLAN {
  142. # Set device.
  143. var("wlan0") dev;
  144. # Wait for device and rfkill switch.
  145. net.backend.waitdevice(dev);
  146. net.backend.rfkill("wlan", dev);
  147. # Start wpa_supplicant on this interface, using configuration in /etc/wpa_supplicant/all.conf .
  148. list() args;
  149. net.backend.wpa_supplicant(dev, "/etc/wpa_supplicant/all.conf", "/usr/sbin/wpa_supplicant", args) sup;
  150. # wpa_supplicant tells us what network we connected to. Look below for how this can be used to
  151. # have different configurations, "BadVPN, but configured differently based on what network we're in".
  152. println("connected to wireless network: bssid=", sup.bssid, " ssid=", sup.ssid);
  153. # Wireless connection successful, here comes network config (DHCP/static/whatever) ...
  154. }
  155. #
  156. # A BadVPN VPN interface for access to the virtual network (only).
  157. #
  158. process lan {
  159. ... (something like above) ...
  160. # Alias our IP address for easy access from the "vpn" process (or, for a static address, alias
  161. # it before assigning it, and assign it using the alias).
  162. var(dhcp.addr) ipaddr;
  163. # Allow VPN to start at this point.
  164. # (and require it to stop before deconfiguring the interface if e.g. the cable is plugged out)
  165. provide("LAN");
  166. }
  167. process vpn {
  168. # Need the local interface to be working in order start VPN.
  169. depend("LAN") landep;
  170. # Choose the name of the network interface.
  171. var("tap3") dev;
  172. # Construct command line arguments for badvpn-client. Adapt according to your setup.
  173. # "--tapdev" will be provided automatically.
  174. # Alias the port number that the VPN process will bind to.
  175. var("6000") port;
  176. # Construct dynamic parts of command line options.
  177. # The VPN client program needs to know some IP addresses in order to tell other peers where to connect to.
  178. # Obtain this informations from variables in the "lan" process through the depend() statement.
  179. # Construct the local address (addr + port).
  180. concat(landep.ipaddr, ":", port) local_addr_arg;
  181. # Construct the Internet address (assuming we are behind a NAT).
  182. # Need to know the NAT's external address here. But we could queried it somehow.
  183. # That is if we have preconfigured the NAT router to forward ports. But we could implement a statement
  184. # that obtains the mappings dynamically with UPnP!
  185. concat("1.2.3.4", ":", port) internet_addr_arg;
  186. # Finally construct the complete arguments, using the above address arguments.
  187. list(
  188. "--logger", "syslog", "--syslog-ident", "badvpn",
  189. "--server-addr", "badvpn.example.com:7000",
  190. "--ssl", "--nssdb", "sql:/home/badvpn/nssdb", "--client-cert-name", "peer-someone",
  191. "--transport-mode", "udp", "--encryption-mode", "blowfish", "--hash-mode", "md5", "--otp", "blowfish", "3000", "2000",
  192. "--scope", "mylan", "--scope", "internet",
  193. "--bind-addr", "0.0.0.0:6000", "--num-ports", "20",
  194. "--ext-addr", local_addr_arg, "mylan",
  195. "--ext-addr", internet_addr_arg, "internet"
  196. ) args;
  197. # Start the BadVPN backend.
  198. # "badvpn" is the user account which the VPN client will run as.
  199. # If you use SSL, the NSS database must be accessible to this user.
  200. net.backend.badvpn(dev, "badvpn", "/usr/bin/badvpn-client-26", args);
  201. # Assign an IP address to the VPN interface.
  202. # (we could easily use DHCP here!)
  203. net.ipv4.addr(dev, "10.0.0.1", "24");
  204. }
  205. #
  206. # BadVPN, but configured differently based on what network we're in.
  207. # The network is identified based on the IP address we were assigned by DHCP.
  208. # The different configuration provide specific arguents to badvpn-client.
  209. #
  210. process lan {
  211. ... (interface config stuff using DHCP, see above) ...
  212. ... (the 'ipaddr' variable holds the local IP address) ...
  213. # Match the address to various known networks.
  214. ip_in_network(ipaddr, "192.168.4.0", "24") is_lan1;
  215. ip_in_network(ipaddr, "192.168.7.0", "24") is_lan2;
  216. # Allow VPN to start at this point.
  217. provide("LAN");
  218. }
  219. process vpn {
  220. ...
  221. # Construct common arguments here ...
  222. list( ... ) common_args;
  223. # Choose appropriate configuration by waking up the configuration processes
  224. # and waiting for one to complete.
  225. provide("VPN_CONF_START");
  226. depend("VPN_CONF_END") config;
  227. # Concatenate common and configuration-specific arguments.
  228. concatlist(common_args, config.args) args;
  229. ...
  230. }
  231. process vpn_config_lan1 {
  232. depend("VPN_CONF_START") dep;
  233. # Proceed only if we're in lan1.
  234. if(dep.landep.is_lan1);
  235. list(
  236. ...
  237. ) args;
  238. provide("VPN_CONF_END");
  239. }
  240. process vpn_config_lan2 {
  241. depend("VPN_CONF_START") dep;
  242. # Proceed only if we're in lan2.
  243. if(dep.landep.is_lan2);
  244. list(
  245. ...
  246. ) args;
  247. provide("VPN_CONF_END");
  248. }
  249. process vpn_config_inet {
  250. depend("VPN_CONF_START") dep;
  251. # Proceed only if we're not in any known network.
  252. ifnot(dep.landep.is_lan1);
  253. ifnot(dep.landep.is_lan2);
  254. list(
  255. ...
  256. ) args;
  257. provide("VPN_CONF_END");
  258. }
  259. #
  260. # Two wired network interfaces (eth0, eth1), both of which may be used for Internet access.
  261. # When both are working, give priority to eth1 (e.g. if eth0 is up, but later eth1 also comes
  262. # up, the configuration will be changed to use eth1 for Internet access).
  263. #
  264. process eth0 {
  265. # Set device.
  266. var("eth0") dev;
  267. # Wait for device.
  268. net.backend.waitdevice(dev);
  269. net.up(dev);
  270. net.backend.waitlink(dev);
  271. # DHCP configuration.
  272. net.ipv4.dhcp(dev) dhcp;
  273. ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local;
  274. ifnot(test_local);
  275. var(dhcp.addr) addr;
  276. var(dhcp.prefix) addr_prefix;
  277. var(dhcp.gateway) gateway;
  278. var(dhcp.dns_servers) dns_servers;
  279. # Assign IP address.
  280. net.ipv4.addr(dev, addr, addr_prefix);
  281. # Go on configuring the network.
  282. multiprovide("NET-eth0");
  283. }
  284. process eth1 {
  285. # Set device.
  286. var("eth1") dev;
  287. # Wait for device.
  288. net.backend.waitdevice(dev);
  289. net.up(dev);
  290. net.backend.waitlink(dev);
  291. # Static configuration.
  292. var("192.168.111.116") addr;
  293. var("24") addr_prefix;
  294. var("192.168.111.1") gateway;
  295. list("192.168.111.14", "193.2.1.66") dns_servers;
  296. # Assign IP address.
  297. net.ipv4.addr(dev, addr, addr_prefix);
  298. # Go on configuring the network.
  299. multiprovide("NET-eth1");
  300. }
  301. process NETCONF {
  302. # Wait for some network connection. Prefer eth1 by putting it in front of eth0.
  303. list("NET-eth1", "NET-eth0") pnames;
  304. multidepend(pnames) ifdep;
  305. # Alias device values.
  306. var(ifdep.dev) dev;
  307. var(ifdep.addr) addr;
  308. var(ifdep.addr_prefix) addr_prefix;
  309. var(ifdep.gateway) gateway;
  310. var(ifdep.dns_servers) dns_servers;
  311. # Add default route.
  312. net.ipv4.route("0.0.0.0", "0", gateway, "20", dev);
  313. # Configure DNS servers.
  314. net.dns(dns_servers, "20");
  315. }