blob: ae944175609b7e5a8986926fa70a6a0de642731f [file] [log] [blame]
Alexander Afanasyeva8bc0d82016-01-25 17:25:30 -08001/*
2 * jndn-management
3 * Copyright (c) 2015-2016, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 3, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
12 * more details.
13 */
14package com.intel.jndn.management;
15
16import com.intel.jndn.management.enums.RouteOrigin;
17import com.intel.jndn.management.helpers.FetchHelper;
18import com.intel.jndn.management.helpers.StatusDatasetHelper;
19import net.named_data.jndn.*;
20import com.intel.jndn.management.types.ControlResponse;
21import com.intel.jndn.management.types.FaceStatus;
22import com.intel.jndn.management.types.FibEntry;
23import com.intel.jndn.management.types.ForwarderStatus;
24import com.intel.jndn.management.enums.LocalControlHeader;
25import com.intel.jndn.management.types.RibEntry;
26import com.intel.jndn.management.types.StrategyChoice;
27import java.io.IOException;
28import java.util.List;
29
30import net.named_data.jndn.encoding.EncodingException;
31import net.named_data.jndn.security.SecurityException;
32
33/**
34 * Helper class for interacting with an NDN forwarder daemon; see
35 * <a href="http://redmine.named-data.net/projects/nfd/wiki/Management">NFD Management</a>
36 * for explanations of the various protocols used.
37 *
38 * @author Andrew Brown <andrew.brown@intel.com>
39 */
40public class Nfdc {
41 private static final int OK_STATUS = 200;
42
43 /////////////////////////////////////////////////////////////////////////////
44
45 /**
46 * Prevent creation of Nfdc instances
47 */
48 private Nfdc() {
49 }
50
51 /**
52 * Retrieve the status of the given forwarder; calls /localhost/nfd/status/general
53 * which requires a local Face (all non-local packets are dropped)
54 *
55 * @param face only a localhost Face
56 * @return the forwarder status object
57 *
58 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus">ForwarderStatus</a>
59 * @throws ManagementException if the network request failed or the returned status could not be decoded
60 */
61 public static ForwarderStatus getForwarderStatus(Face face) throws ManagementException {
62 try {
63 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/status/general"));
64 return new ForwarderStatus(StatusDatasetHelper.combine(segments));
65 } catch (IOException|EncodingException e) {
66 throw new ManagementException(e.getMessage(), e);
67 }
68 }
69
70 /**
71 * Retrieve a list of faces and their status from the given forwarder; calls
72 * /localhost/nfd/faces/list which requires a local Face (all non-local
73 * packets are dropped)
74 *
75 * @param face only a localhost Face
76 * @return a list of face status objects
77 *
78 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt">FaceManagement</a>
79 * @throws ManagementException if the network request failed or if the NFD rejected the request
80 */
81 public static List<FaceStatus> getFaceList(Face face) throws ManagementException {
82 try {
83 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/faces/list"));
84 return StatusDatasetHelper.wireDecode(segments, FaceStatus.class);
85 } catch (IOException e) {
86 throw new ManagementException(e.getMessage(), e);
87 }
88 }
89
90 /**
91 * Retrieve a list of FIB entries and their NextHopRecords from the given
92 * forwarder; calls /localhost/nfd/fib/list which requires a local Face (all
93 * non-local packets are dropped).
94 *
95 * @param face only a localhost Face
96 * @return a list of FIB entries
97 *
98 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset">FIB Dataset</a>
99 * @throws ManagementException if the network request failed or if the NFD rejected the request
100 */
101 public static List<FibEntry> getFibList(Face face) throws ManagementException {
102 try {
103 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/fib/list"));
104 return StatusDatasetHelper.wireDecode(segments, FibEntry.class);
105 } catch (IOException e) {
106 throw new ManagementException(e.getMessage(), e);
107 }
108 }
109
110 /**
111 * Retrieve a list of routing entries from the RIB; calls
112 * /localhost/nfd/rib/list which requires a local Face (all non-local packets
113 * are dropped).
114 *
115 * @param face only a localhost Face
116 * @return a list of RIB entries, i.e. routes
117 *
118 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset">RIB Dataset</a>
119 * @throws ManagementException if the network request failed or if the NFD rejected the request
120 */
121 public static List<RibEntry> getRouteList(Face face) throws ManagementException {
122 try {
123 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/rib/list"));
124 return StatusDatasetHelper.wireDecode(segments, RibEntry.class);
125 } catch (IOException e) {
126 throw new ManagementException(e.getMessage(), e);
127 }
128 }
129
130 /**
131 * Retrieve the list of strategy choice entries from the NFD; calls
132 * /localhost/nfd/rib/list which requires a local Face (all non-local packets
133 * are dropped).
134 *
135 * @param face only a localhost Face
136 * @return a list of strategy choice entries, i.e. routes
137 *
138 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">StrategyChoice</a>
139 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
140 * the NFD rejected the request
141 */
142 public static List<StrategyChoice> getStrategyList(Face face) throws ManagementException {
143 try {
144 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/strategy-choice/list"));
145 return StatusDatasetHelper.wireDecode(segments, StrategyChoice.class);
146 } catch (IOException e) {
147 throw new ManagementException(e.getMessage(), e);
148 }
149 }
150
151 /**
152 * Retrieve the {@link KeyLocator} for an NFD.
153 *
154 * @param face only a localhost {@link Face}
155 * @return the {@link KeyLocator} of the NFD's key
156 * @throws ManagementException if the network request failed, if the NFD rejected the request, or no
157 * KeyLocator was found
158 */
159 public static KeyLocator getKeyLocator(Face face) throws ManagementException {
160 try {
161 List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/status/general"));
162 if (segments.isEmpty() || !KeyLocator.canGetFromSignature(segments.get(0).getSignature())) {
163 throw new ManagementException("No key locator available.");
164 }
165 return KeyLocator.getFromSignature(segments.get(0).getSignature());
166 } catch (IOException e) {
167 throw new ManagementException(e.getMessage(), e);
168 }
169 }
170
171 /**
172 * Create a new face on the given forwarder. Ensure the forwarding face is on
173 * the local machine (management requests are to /localhost/...) and that
174 * command signing has been set up (e.g. forwarder.setCommandSigningInfo()).
175 *
176 * @param face only a localhost {@link Face}
177 * @param uri a string like "tcp4://host.name.com" (see nfd-status channels
178 * for more protocol options)
179 * @return the newly created face ID
180 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
181 * the NFD rejected the request
182 */
183 public static int createFace(Face face, String uri) throws ManagementException {
184 Name command = new Name("/localhost/nfd/faces/create");
185 ControlParameters parameters = new ControlParameters();
186 parameters.setUri(uri);
187 command.append(parameters.wireEncode());
188
189 try {
190 // send the interest
191 ControlResponse response = sendCommand(face, command);
192
193 // return
194 return response.getBody().get(0).getFaceId();
195 }
196 catch (IOException|EncodingException e) {
197 throw new ManagementException(e.getMessage(), e);
198 }
199 }
200
201 /**
202 * Destroy a face on given forwarder. Ensure the forwarding face is on the
203 * local machine (management requests are to /localhost/...) and that command
204 * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
205 *
206 * @param face only a localhost {@link Face}
207 * @param faceId the ID of the face to destroy
208 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
209 * the NFD rejected the request
210 */
211 public static void destroyFace(Face face, int faceId) throws ManagementException {
212 Name command = new Name("/localhost/nfd/faces/destroy");
213 ControlParameters parameters = new ControlParameters();
214 parameters.setFaceId(faceId);
215 command.append(parameters.wireEncode());
216
217 try {
218 sendCommand(face, command);
219 } catch (IOException|EncodingException|ManagementException e) {
220 throw new ManagementException(e.getMessage(), e);
221 }
222 }
223
224 /**
225 * Enable a local control feature on the given forwarder. See
226 * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature</a>
227 *
228 * @param face only a localhost {@link Face}
229 * @param header the control feature to enable
230 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
231 * the NFD rejected the request
232 */
233 public static void enableLocalControlHeader(Face face, LocalControlHeader header) throws ManagementException {
234 // build command name
235 Name command = new Name("/localhost/nfd/faces/enable-local-control");
236 ControlParameters parameters = new ControlParameters();
237 parameters.setLocalControlFeature(header.toInteger());
238 command.append(parameters.wireEncode());
239
240 try {
241 sendCommand(face, command);
242 } catch (IOException|EncodingException|ManagementException e) {
243 throw new ManagementException(e.getMessage(), e);
244 }
245 }
246
247 /**
248 * Disable a local control feature on the given forwarder. See
249 * <a href="http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature">http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature</a>
250 *
251 * @param face only a localhost {@link Face}
252 * @param header the control feature to disable
253 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
254 * the NFD rejected the request
255 */
256 public static void disableLocalControlHeader(Face face, LocalControlHeader header) throws ManagementException {
257 // build command name
258 Name command = new Name("/localhost/nfd/faces/disable-local-control");
259 ControlParameters parameters = new ControlParameters();
260 parameters.setLocalControlFeature(header.toInteger());
261 command.append(parameters.wireEncode());
262
263 try {
264 sendCommand(face, command);
265 } catch (IOException|EncodingException|ManagementException e) {
266 throw new ManagementException(e.getMessage(), e);
267 }
268 }
269
270 /**
271 * Register a route on the forwarder; see
272 * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
273 * for command-line usage and
274 * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
275 * for protocol documentation. Ensure the forwarding face is on the local
276 * machine (management requests are to /localhost/...) and that command
277 * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
278 *
279 * @param face only a localhost {@link Face}
280 * @param controlParameters the {@link ControlParameters} command options
281 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
282 * the NFD rejected the request
283 */
284 public static void register(Face face, ControlParameters controlParameters) throws ManagementException {
285 // build command name
286 Name command = new Name("/localhost/nfd/rib/register");
287 command.append(controlParameters.wireEncode());
288
289 try {
290 sendCommand(face, command);
291 } catch (IOException|EncodingException|ManagementException e) {
292 throw new ManagementException(e.getMessage(), e);
293 }
294 }
295
296 /**
297 * Register a route on a forwarder; this will create a new face on the
298 * forwarder towards the face (e.g., self registration)
299 *
300 * @param face only a localhost {@link Face}
301 * @param route the {@link Name} prefix of the route
302 * @param cost the numeric cost of forwarding along the route
303 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
304 * the NFD rejected the request
305 */
306 public static void register(Face face, Name route, int cost) throws ManagementException {
307 ForwardingFlags flags = new ForwardingFlags();
308 flags.setCapture(false);
309 flags.setChildInherit(true);
310
311 register(face, new ControlParameters()
312 .setName(route)
313 .setCost(cost)
314 .setOrigin(RouteOrigin.APP.toInteger())
315 .setForwardingFlags(flags));
316 }
317 /**
318 * Register a route on a forwarder; this will create a new face on the
319 * forwarder to the given URI/route pair. See register(Face,
320 * ControlParameters) for more detailed documentation.
321 *
322 * @param face only a localhost {@link Face}
323 * @param uri the URI (e.g. "tcp4://10.10.2.2:6363") of the remote node; note
324 * that this must be one of the canonical forms described in the wiki
325 * (http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#TCP) for NFD to
326 * accept the registration--otherwise you will see 400 errors
327 * @param route the {@link Name} prefix of the route
328 * @param cost the numeric cost of forwarding along the route
329 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
330 * the NFD rejected the request
331 */
332 public static void register(Face face, String uri, Name route, int cost) throws ManagementException {
333 // create the new face
334 int faceId = createFace(face, uri);
335
336 // run base method
337 register(face, faceId, route, cost);
338 }
339
340 /**
341 * Register a route on a forwarder; this will not create a new face since it
342 * is provided a faceId. See register(Face, ControlParameters) for full
343 * documentation.
344 *
345 * @param forwarder only a localhost {@link Face}
346 * @param faceId the ID of the {@link Face} to assign to the route
347 * @param route the {@link Name} prefix of the route
348 * @param cost the numeric cost of forwarding along the route
349 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
350 * the NFD rejected the request
351 */
352 public static void register(Face forwarder, int faceId, Name route, int cost) throws ManagementException {
353 // build command name
354 ControlParameters parameters = new ControlParameters();
355 parameters.setName(route);
356 parameters.setFaceId(faceId);
357 parameters.setCost(cost);
358 parameters.setOrigin(RouteOrigin.STATIC.toInteger());
359 ForwardingFlags flags = new ForwardingFlags();
360 flags.setCapture(false);
361 flags.setChildInherit(true);
362 parameters.setForwardingFlags(flags);
363
364 // run base method
365 register(forwarder, parameters);
366 }
367
368 /**
369 * Unregister a route on a forwarder; see
370 * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
371 * for command-line usage and
372 * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
373 * for protocol documentation. Ensure the forwarding face is on the local
374 * machine (management requests are to /localhost/...) and that command
375 * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
376 *
377 * @param face only a localhost {@link Face}
378 * @param controlParameters the {@link ControlParameters} command options
379 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
380 * the NFD rejected the request
381 */
382 public static void unregister(Face face, ControlParameters controlParameters) throws ManagementException {
383 // build command name
384 Name command = new Name("/localhost/nfd/rib/unregister");
385 command.append(controlParameters.wireEncode());
386
387 try {
388 sendCommand(face, command);
389 } catch (IOException|EncodingException|ManagementException e) {
390 throw new ManagementException(e.getMessage(), e);
391 }
392 }
393
394 /**
395 * Unregister a route on a forwarder; see
396 * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
397 * for command-line usage and
398 * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
399 * for protocol documentation. Ensure the forwarding face is on the local
400 * machine (management requests are to /localhost/...) and that command
401 * signing has been set up (e.g. forwarder.setCommandSigningInfo().
402 *
403 * @param face only a localhost {@link Face}
404 * @param route the {@link Name} prefix of the route
405 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
406 * the NFD rejected the request
407 */
408 public static void unregister(Face face, Name route) throws ManagementException {
409 // build command name
410 ControlParameters controlParameters = new ControlParameters();
411 controlParameters.setName(route);
412
413 // send the interest
414 unregister(face, controlParameters);
415 }
416
417 /**
418 * Unregister a route on a forwarder; see
419 * <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">http://named-data.net/doc/NFD/current/manpages/nfdc.html</a>
420 * for command-line usage and
421 * <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">http://redmine.named-data.net/projects/nfd/wiki/RibMgmt</a>
422 * for protocol documentation. Ensure the forwarding face is on the local
423 * machine (management requests are to /localhost/...) and that command
424 * signing has been set up (e.g. forwarder.setCommandSigningInfo().
425 *
426 * @param face only a localhost {@link Face}
427 * @param route the {@link Name} prefix of the route
428 * @param faceId the specific ID of the face to remove (more than one face can
429 * be registered to a route)
430 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
431 * the NFD rejected the request
432 */
433 public static void unregister(Face face, Name route, int faceId) throws ManagementException {
434 // build command name
435 ControlParameters controlParameters = new ControlParameters();
436 controlParameters.setName(route);
437 controlParameters.setFaceId(faceId);
438
439 // send the interest
440 unregister(face, controlParameters);
441 }
442
443 /**
444 * Unregister a route on a forwarder
445 *
446 * Ensure the forwarding face is on the local machine (management requests are to /localhost/...) and that command
447 * signing has been set up using forwarder.setCommandSigningInfo().
448 *
449 * @param face only a localhost {@link Face}
450 * @param route the {@link Name} prefix of the route
451 * @param uri the URI (e.g. "tcp4://some.host.com") of the remote node (more
452 * than one face can be registered to a route)
453 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
454 * the NFD rejected the request
455 * @see <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">nfdc command-line usage</a>
456 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/RibMgmt">RibMgmt</a>
457 */
458 public static void unregister(Face face, Name route, String uri) throws ManagementException {
459 int faceId = -1;
460 for (FaceStatus faceStatus : getFaceList(face)) {
461 if (faceStatus.getRemoteUri().matches(uri)) {
462 faceId = faceStatus.getFaceId();
463 break;
464 }
465 }
466
467 if (faceId == -1) {
468 throw new ManagementException("Face not found: " + uri);
469 }
470
471 // send the interest
472 unregister(face, route, faceId);
473 }
474
475 /**
476 * Set a strategy on the forwarder
477 *
478 * Ensure the forwarding face is on the local machine (management requests are to /localhost/...) and that command
479 * signing has been set up using forwarder.setCommandSigningInfo().
480 *
481 * @param face only a localhost {@link Face}
482 * @param prefix the {@link Name} prefix
483 * @param strategy the {@link Name} of the strategy to set, e.g.
484 * /localhost/nfd/strategy/broadcast
485 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
486 * the NFD rejected the request
487 * @see <a href="http://named-data.net/doc/NFD/current/manpages/nfdc.html">nfdc command-line usage</a>
488 * @see <a href="http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice">StrategyChoice</a>
489 */
490 public static void setStrategy(Face face, Name prefix, Name strategy) throws ManagementException {
491 // build command name
492 Name command = new Name("/localhost/nfd/strategy-choice/set");
493 ControlParameters parameters = new ControlParameters();
494 parameters.setName(prefix);
495 parameters.setStrategy(strategy);
496 command.append(parameters.wireEncode());
497
498 try {
499 sendCommand(face, command);
500 } catch (IOException|EncodingException|ManagementException e) {
501 throw new ManagementException(e.getMessage(), e);
502 }
503 }
504
505 /**
506 * Set a strategy on the forwarder; see
507 * {@link #setStrategy(net.named_data.jndn.Face, net.named_data.jndn.Name, net.named_data.jndn.Name)}
508 * for more information. Ensure the forwarding face is on the local machine
509 * (management requests are to /localhost/...) and that command signing has
510 * been set up (e.g. forwarder.setCommandSigningInfo()).
511 *
512 * @param face only a localhost {@link Face}
513 * @param prefix the {@link Name} prefix
514 * @throws ManagementException if the network request failed, the NFD response could not be decoded, or
515 * the NFD rejected the request
516 */
517 public static void unsetStrategy(Face face, Name prefix) throws ManagementException {
518 // build command name
519 Name command = new Name("/localhost/nfd/strategy-choice/unset");
520 ControlParameters parameters = new ControlParameters();
521 parameters.setName(prefix);
522 command.append(parameters.wireEncode());
523
524 try {
525 sendCommand(face, command);
526 } catch (IOException|EncodingException|ManagementException e) {
527 throw new ManagementException(e.getMessage(), e);
528 }
529 }
530
531 /**
532 * Send an interest as a command to the forwarder; this method will convert
533 * the interest to a command interest and block until a response is received
534 * from the forwarder. Ensure the forwarding face is on the local machine
535 * (management requests are to /localhost/...) and that command signing has
536 * been set up (e.g. forwarder.setCommandSigningInfo()).
537 *
538 * @param face only a localhost Face, command signing info must be set
539 * @param name As described at
540 * <a href="http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,">http://redmine.named-data.net/projects/nfd/wiki/ControlCommand,</a>
541 * the requested interest must have encoded ControlParameters appended to the
542 * interest name
543 * @return a {@link ControlResponse}
544 * @throws java.io.IOException
545 * @throws net.named_data.jndn.encoding.EncodingException
546 * @throws ManagementException
547 */
548 private static ControlResponse
549 sendCommand(Face face, Name name) throws IOException, EncodingException, ManagementException {
550 Interest interest = new Interest(name);
551
552 // forwarder must have command signing info set
553 try {
554 face.makeCommandInterest(interest);
555 } catch (SecurityException e) {
556 throw new IllegalArgumentException("Failed to make command interest; ensure command signing info is set on the face.", e);
557 }
558
559 // send command packet
560 Data data = FetchHelper.getData(face, interest.getName());
561
562 // decode response
563 ControlResponse response = new ControlResponse();
564 response.wireDecode(data.getContent().buf());
565
566 // check response for success
567 if (response.getStatusCode() != OK_STATUS) {
568 throw ManagementException.fromResponse(response);
569 }
570
571 return response;
572 }
573}