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