BgpRouter code enhancements
[netvirt.git] / bgpmanager / impl / src / main / java / org / opendaylight / netvirt / bgpmanager / ConfigureBgpCli.java
1 /*
2  * Copyright © 2015, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netvirt.bgpmanager;
10
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.List;
14 import org.apache.karaf.shell.commands.Command;
15 import org.apache.karaf.shell.commands.Option;
16 import org.apache.karaf.shell.console.OsgiCommandSupport;
17 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_afi;
18 import org.opendaylight.netvirt.bgpmanager.thrift.gen.af_safi;
19 import org.opendaylight.netvirt.bgpmanager.thrift.gen.protocol_type;
20 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.Bgp;
21 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.TcpMd5SignaturePasswordType;
22 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.bgp.Neighbors;
23 import org.opendaylight.yangtools.yang.common.Uint32;
24
25 @Command(scope = "odl", name = "configure-bgp", description = "")
26 public class ConfigureBgpCli extends OsgiCommandSupport {
27     private static final long AS_MIN = 0;
28     private static final long AS_MAX = 4294967295L;//2^32-1
29
30     @Option(name = "-op", aliases = {"--operation", "--op"}, description = "[start-bgp-server, stop-bgp-server, "
31             + "add-neighbor, delete-neighbor, add-route, delete-route,graceful-restart, enable-log ]",
32             required = false, multiValued = false)
33     String op;
34
35     //exec configure-bgp  add-neighbor --ip <neighbor-ip> --as-num <as-num> --address-family <af> --use-source-ip
36     // <sip> --ebgp-multihops <em> --next-hop <nh>
37     //exec configure-bgp --op add-route/delete-route --rd <rd> --prefix <prefix> --nexthop <nexthop>
38     // --mac <mac> --l2vni <l2vni> --l3vni <l3vni>
39
40     @Option(name = "--as-num", description = "as number of the bgp neighbor", required = false, multiValued = false)
41     String asNumber = null;
42
43     @Option(name = "--ip", description = "ip of the bgp neighbor", required = false, multiValued = false)
44     String ip = null;
45
46     @Option(name = "--tcp-md5-password", description = "RFC2385 TCP MD5 Signature Option shared secret",
47             required = false, multiValued = false)
48     String md5passwordOption = null;
49
50     @Option(name = "--address-family", description = "address family of the bgp neighbor "
51             + "lu|evpn|vpnv4|vpnv6",
52             required = false, multiValued = false)
53     String addressFamily = null;
54
55     @Option(name = "--use-source-ip", description = "source ip to be used for neighborship connection establishment",
56             required = false, multiValued = false)
57     String sourceIp = null;
58
59     @Option(name = "--ebgp-multihops", description = "ebgp multihops of the bgp neighbor",
60             required = false, multiValued = false)
61     String ebgpMultihops = null;
62
63     @Option(name = "--router-id", description = "router id of the bgp instance",
64             required = false, multiValued = false)
65     String routerId = null;
66
67     @Option(name = "--rd", description = "rd of the route",
68             required = false, multiValued = false)
69     String rd = null;
70
71     @Option(name = "--prefix", description = "prefix of the route",
72             required = false, multiValued = false)
73     String prefix = null;
74
75     @Option(name = "--nexthop", description = "nexthop of the route",
76             required = false, multiValued = false)
77     String nexthop = null;
78
79     @Option(name = "--mac", description = "mac of the route",
80             required = false, multiValued = false)
81     String mac = null;
82
83     @Option(name = "--l2vni", description = "l2vni of the route",
84             required = false, multiValued = false)
85     Uint32 l2vni = Uint32.ZERO;
86
87     @Option(name = "--l3vni", description = "l3vni",
88             required = false, multiValued = false)
89     Uint32 l3vni = Uint32.ZERO;
90
91     @Option(name = "--stalepath-time", description = "the time delay after bgp restart stalepaths are cleaned",
92             required = false, multiValued = false)
93     String stalePathTime = null;
94
95     @Option(name = "--log-file-path", description = "bgp log file path",
96             required = false, multiValued = false)
97     String logFile = null;
98
99     @Option(name = "--log-level", description = "log level emergencies,alerts,critical,errors,warnings,notifications,"
100             + "informational,debugging",
101             required = false, multiValued = false)
102     String logLevel = null;
103
104     enum LogLevels {
105         emergencies, alerts, critical, errors, warnings, notifications, informational, debugging
106     }
107
108     private final BgpManager bgpManager;
109     private final BgpConfigurationManager bgpConfigurationManager;
110
111     public ConfigureBgpCli(BgpManager bgpManager, BgpConfigurationManager bgpConfigurationManager) {
112         this.bgpManager = bgpManager;
113         this.bgpConfigurationManager = bgpConfigurationManager;
114     }
115
116     @Override
117     protected Object doExecute() throws Exception {
118         if (op == null) {
119             session.getConsole().println("Please provide valid operation");
120             usage();
121             session.getConsole().println(
122                     "exec configure-bgp -op [start-bgp-server | stop-bgp-server | add-neighbor | delete-neighbor|"
123                             + " add-route | delete-route | graceful-restart| enable-log ]");
124             return null;
125         }
126         switch (op) {
127             case "start-bgp-server":
128                 startBgp();
129                 break;
130             case "stop-bgp-server":
131                 stopBgp();
132                 break;
133             case "add-neighbor":
134                 addNeighbor();
135                 break;
136             case "delete-neighbor":
137                 deleteNeighbor();
138                 break;
139             case "add-route":
140                 addRoute();
141                 break;
142             case "delete-route":
143                 deleteRoute();
144                 break;
145             case "graceful-restart":
146                 configureGR();
147                 break;
148             case "enable-log":
149                 enableBgpLogLevel();
150                 break;
151             default:
152                 session.getConsole().println("invalid operation");
153                 usage();
154                 session.getConsole().println(
155                         "exec configure-bgp -op [start-bgp-server | stop-bgp-server | add-neighbor | "
156                                 + "delete-neighbor| graceful-restart| enable-log ]");
157         }
158         return null;
159     }
160
161     public boolean validateStalepathTime() {
162         try {
163             int time = Integer.parseInt(stalePathTime);
164             if (time < 30 || time > 3600) {
165                 session.getConsole().println("invalid stale path time valid range [30-3600]" + stalePathTime);
166                 printGracefulRestartHelp();
167                 return false;
168             }
169         } catch (NumberFormatException e) {
170             session.getConsole().println("invalid stale path time" + stalePathTime);
171             printGracefulRestartHelp();
172             return false;
173         }
174         return true;
175     }
176
177     private void configureGR() {
178         boolean validStalepathTime = validateStalepathTime();
179         if (!validStalepathTime) {
180             return;
181         }
182         bgpManager.configureGR(Integer.parseInt(stalePathTime));
183     }
184
185     private void deleteNeighbor() {
186         if (ip == null || !validateIp(ip)) {
187             session.getConsole().println("invalid neighbor ip");
188             printDeleteNeighborHelp();
189             return;
190         }
191         long asNo = getAsNumber(ip);
192         if (asNo < 0) {
193             session.getConsole().println("neighbor does not exist");
194             printDeleteNeighborHelp();
195             return;
196         }
197         bgpManager.deleteNeighbor(ip);
198     }
199
200     public long getAsNumber(String nbrIp) {
201         Bgp conf = bgpManager.getConfig();
202         if (conf == null) {
203             return -1;
204         }
205         List<Neighbors> nbrs = conf.getNeighbors();
206         if (nbrs == null) {
207             return -1;
208         }
209         for (Neighbors nbr : nbrs) {
210             if (nbrIp.equals(nbr.getAddress().getValue())) {
211                 return nbr.getRemoteAs().toJava();
212             }
213         }
214         return -1;
215     }
216
217     private void stopBgp() {
218         Bgp conf = bgpManager.getConfig();
219         if (conf == null) {
220             return;
221         }
222         List<Neighbors> nbrs = conf.getNeighbors();
223         if (nbrs != null && nbrs.size() > 0) {
224             session.getConsole().println(
225                     "error: all BGP congiguration must be deleted before stopping the router instance");
226             return;
227         }
228         bgpManager.stopBgp();
229     }
230
231     private void usage() {
232         session.getConsole().println("usage:");
233     }
234
235     private void printStartBgpHelp() {
236         usage();
237         session.getConsole().println(
238                 "exec configure-bgp -op start-bgp-server --as-num <asnum> --router-id <routerid> [--stalepath-time "
239                         + "<time>]");
240     }
241
242     private void printAddNeighborHelp() {
243         usage();
244         session.getConsole().println(
245                 "exec configure-bgp -op add-neighbor --ip <neighbor-ip> --as-num <as-num> [--address-family <af>] "
246                         + "[--tcp-md5-password <password>] "
247                         + "[--use-source-ip <sip>] [--ebgp-multihops <em> ]");
248     }
249
250     private void printDeleteNeighborHelp() {
251         usage();
252         session.getConsole().println("exec configure-bgp -op delete-neighbor --ip <neighbor-ip>");
253     }
254
255     void printEnableLogHelp() {
256         usage();
257         session.getConsole().println(
258                 "exec configure-bgp -op enable-logging --filename <filename> --log-level "
259                         + "[emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]");
260     }
261
262     private void printGracefulRestartHelp() {
263         usage();
264         session.getConsole().println("exec configure-bgp -op graceful-restart --stalepath-time <30-3600>");
265     }
266
267     private void startBgp() {
268         boolean validRouterId = false;
269
270         if (bgpManager.getConfig() != null && bgpManager.getConfig().getAsId() != null) {
271             session.getConsole().println("bgp is already started please use stop-bgp-server and start again");
272             return;
273         }
274         if (!validateAsNumber(asNumber)) {
275             printStartBgpHelp();
276             return;
277         }
278         validRouterId = validateIp(routerId);
279         if (!validRouterId) {
280             session.getConsole().println("invalid router id please supply valid ip address");
281             printStartBgpHelp();
282             return;
283         }
284
285         if (stalePathTime != null) {
286             boolean validStalepathTime = validateStalepathTime();
287             if (!validStalepathTime) {
288                 return;
289             }
290         }
291         bgpManager.startBgp(Long.parseLong(asNumber), routerId,
292                 stalePathTime == null ? 0 : Integer.parseInt(stalePathTime), false);
293     }
294
295     protected void addNeighbor() {
296         if (!validateAsNumber(asNumber)) {
297             printAddNeighborHelp();
298             return;
299         }
300
301         boolean validIp = validateIp(ip);
302         if (!validIp) {
303             session.getConsole().println("invalid neighbor ip");
304             printAddNeighborHelp();
305             return;
306         }
307
308         TcpMd5SignaturePasswordType md5secret = null;
309         if (md5passwordOption != null) {
310             try {
311                 md5secret = new TcpMd5SignaturePasswordType(md5passwordOption);
312             } catch (IllegalArgumentException e) {
313                 session.getConsole().println(
314                         new StringBuilder("invalid MD5 password: ").append(e.getMessage()).toString());
315                 printAddNeighborHelp();
316                 return;
317             }
318         }
319
320         if (sourceIp != null) {
321             validIp = validateIp(sourceIp);
322             if (!validIp) {
323                 session.getConsole().println("invalid source ip");
324                 printAddNeighborHelp();
325                 return;
326             }
327         }
328
329         if (ebgpMultihops != null) {
330             try {
331                 long val = Long.parseLong(ebgpMultihops);
332                 if (val < 1 || val > 255) {
333                     session.getConsole().println("invalid ebgpMultihops number , valid range [1,255] "
334                             + ebgpMultihops);
335                     printAddNeighborHelp();
336                     return;
337                 }
338             } catch (NumberFormatException e) {
339                 session.getConsole().println("invalid ebgpMultihops number, valid range [1-255]"
340                         + ebgpMultihops);
341                 printAddNeighborHelp();
342                 return;
343             }
344         }
345         if (addressFamily != null) {
346             if (!addressFamily.equals("lu") && !addressFamily.equals("vpnv4")
347                     && !addressFamily.equals("vpnv6")
348                     && !addressFamily.equals("evpn")) {
349                 session.getConsole().println("error: Address family must be lu/evpn/vpnv4/vpnv6 ");
350                 return;
351             }
352
353             int afi ;
354             int safi ;
355             if (addressFamily.equals("vpnv6")) {
356                 afi = 2;
357                 safi = 5;
358             } else if (addressFamily.equals("evpn")) {
359                 afi = 3;
360                 safi = 6;
361             } else if (addressFamily.equals("lu")) {
362                 afi = 1;
363                 safi = 4;
364             } else if  (addressFamily.equals("vpnv4")) {
365                 afi = 1;
366                 safi = 5;
367             } else {
368                 session.getConsole().println(
369                         "invalid addressFamily valid values lu/evpn/vpnv4/vpnv6");
370                 printAddNeighborHelp();
371                 return ;
372             }
373         }
374         if (getAsNumber(ip) != -1) {
375             session.getConsole().println("neighbor with ip " + ip + " already exists");
376             return;
377         }
378         bgpConfigurationManager.addNeighbor(ip, Long.parseLong(asNumber), md5secret);
379         if (addressFamily != null) {
380             bgpConfigurationManager.addAddressFamily(ip, af_afi.AFI_IP.getValue(),
381                     af_safi.valueOf(addressFamily).getValue());
382         }
383         if (ebgpMultihops != null) {
384             bgpConfigurationManager.addEbgpMultihop(ip, Integer.parseInt(ebgpMultihops));
385         }
386         if (sourceIp != null) {
387             bgpConfigurationManager.addUpdateSource(ip, sourceIp);
388         }
389     }
390
391     protected void addRoute() {
392         bgpConfigurationManager.onUpdatePushRoute(protocol_type.PROTOCOL_L3VPN, rd, prefix,
393                 0, nexthop, mac, l3vni, l2vni, null, null);
394     }
395
396     protected void deleteRoute() {
397         bgpConfigurationManager.onUpdateWithdrawRoute(protocol_type.PROTOCOL_L3VPN, rd, prefix,
398                 0, nexthop, mac);
399     }
400
401     private boolean validateIp(String inputIp) {
402         boolean validIp = false;
403         try {
404             if (inputIp != null) {
405                 InetAddress addr = InetAddress.getByName(inputIp);
406                 if (addr.isMulticastAddress()) {
407                     session.getConsole().println("ip cannot be multicast address");
408                     return false;
409                 }
410                 if (addr.isLoopbackAddress()) {
411                     session.getConsole().println("ip cannot be loopback address");
412                     return false;
413                 }
414                 byte[] addrBytes = addr.getAddress();
415                 int lastPart = addrBytes[3] & 0xFF;
416                 int firstPart = addrBytes[0] & 0xFF;
417                 if (firstPart == 0) {
418                     return false;//cannot start with 0 "0.1.2.3"
419                 }
420                 if (lastPart == 0 || lastPart == 255) {
421                     return false;
422                 }
423                 validIp = true;
424             }
425         } catch (UnknownHostException e) {
426             // Ignored?
427         }
428         return validIp;
429     }
430
431     private void enableBgpLogLevel() {
432         if (logFile == null) {
433             session.getConsole().println("Please provide log file name ");
434             usage();
435             session.getConsole().println(
436                     "exec configure-bgp -op enable-log --log-file-path <logfile> --log-level <level>");
437             return;
438         }
439         boolean validLoglevel = false;
440         try {
441             LogLevels.valueOf(logLevel);
442             validLoglevel = true;
443         } catch (IllegalArgumentException e) {
444             // Ignored?
445         }
446         if (!validLoglevel) {
447             session.getConsole().println(
448                     "Please provide valid log level "
449                             + "emergencies|alerts|critical|errors|warnings|notifications|informational|debugging");
450             usage();
451             session.getConsole().println(
452                     "exec configure-bgp -op enable-log --log-file-path <logfile> --log-level <level>");
453             return;
454         }
455         bgpManager.setQbgpLog(logFile, logLevel);
456     }
457
458     private boolean validateAsNumber(String strAsnum) {
459
460         try {
461             long asNum = Long.parseLong(strAsnum);
462             if (asNum == 0L || asNum == 65535L || asNum == 23456L) {
463                 session.getConsole().println("reserved AS Number supplied ");
464                 return false;
465             }
466             if (asNum <= AS_MIN || asNum > AS_MAX) {
467                 session.getConsole().println("invalid AS Number , supported range [1," + AS_MAX + "]");
468                 return false;
469             }
470         } catch (NumberFormatException e) {
471             session.getConsole().println("invalid AS Number " + asNumber);
472             return false;
473         }
474         return true;
475     }
476 }