blob: b5f531f084f333322c682dc6c75ff94d0277a1de [file] [log] [blame]
Andrew Brown2f1fdbf2015-01-21 10:52:29 -08001/*
2 * File name: NFD.java
3 *
4 * Purpose: Helper class for interacting with an NDN forwarder daemon;
5 * see http://redmine.named-data.net/projects/nfd/wiki/Management for
6 * explanations of the various protocols used.
7 *
8 * © Copyright Intel Corporation. All rights reserved.
9 * Intel Corporation, 2200 Mission College Boulevard,
10 * Santa Clara, CA 95052-8119, USA
11 */
12package com.intel.jndn.management;
13
Andrew Brownc46c1602015-02-18 10:45:56 -080014import com.intel.jndn.management.types.StatusDataset;
15import com.intel.jndn.management.types.ControlResponse;
16import com.intel.jndn.management.types.FaceStatus;
17import com.intel.jndn.management.types.RibEntry;
Andrew Brown2f1fdbf2015-01-21 10:52:29 -080018import com.intel.jndn.utils.Client;
19import java.io.IOException;
20import java.util.List;
21import net.named_data.jndn.ControlParameters;
22import net.named_data.jndn.Data;
23import net.named_data.jndn.Face;
24import net.named_data.jndn.ForwardingFlags;
25import net.named_data.jndn.Interest;
26import net.named_data.jndn.Name;
27import net.named_data.jndn.encoding.EncodingException;
28import net.named_data.jndn.security.SecurityException;
Andrew Brownc46c1602015-02-18 10:45:56 -080029import java.util.logging.Logger;
Andrew Brown2f1fdbf2015-01-21 10:52:29 -080030
31/**
32 * Helper class for interacting with an NDN forwarder daemon; see
33 * http://redmine.named-data.net/projects/nfd/wiki/Management for explanations
34 * of the various protocols used.
35 *
36 * @author Andrew Brown <andrew.brown@intel.com>
37 */
38public class NFD {
39
40 public final static long DEFAULT_TIMEOUT = 2000;
Andrew Brownc46c1602015-02-18 10:45:56 -080041 static private final Logger logger = Logger.getLogger(NFD.class.getName());
Andrew Brown2f1fdbf2015-01-21 10:52:29 -080042
43 /**
44 * Ping a forwarder on an existing face to verify that the forwarder is
45 * working and responding to requests; this version sends a discovery packet
46 * to /localhost/nfd which should always respond if the requestor is on the
47 * same machine as the NDN forwarding daemon.
48 *
Andrew Brownc46c1602015-02-18 10:45:56 -080049 * @param face
Andrew Brown2f1fdbf2015-01-21 10:52:29 -080050 * @return true if successful, false otherwise
51 */
52 public static boolean pingLocal(Face face) {
53 return ping(face, new Name("/localhost/nfd"));
54 }
55
56 /**
57 * Request a name on an existing face to verify the forwarder is working and
58 * responding to requests. Note that the name must be served or cached on
59 * the forwarder for this to return true.
60 *
61 * @param face
62 * @param name
63 * @return true if successful, false otherwise
64 */
65 public static boolean ping(Face face, Name name) {
66 // build interest
67 Interest interest = new Interest(name);
68 interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
69 interest.setMustBeFresh(true);
70
71 // send packet
72 Data data = Client.getDefault().getSync(face, interest);
73 return data != null;
74 }
75
76 /**
77 * Retrieve a list of faces and their status from the given forwarder; calls
78 * /localhost/nfd/faces/list which requires a local Face (all non-local
79 * packets are dropped)
80 *
81 * @param forwarder Only a localhost Face
82 * @return
83 * @throws Exception
84 */
85 public static List<FaceStatus> getFaceList(Face forwarder) throws Exception {
86 // build management Interest packet; see http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
87 Interest interest = new Interest(new Name("/localhost/nfd/faces/list"));
88 interest.setMustBeFresh(true);
89 interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
90 interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
91
92 // TODO verify that all faces are being returned; right now they don't
93 // match up with the results from nfd-status-http-server but no
94 // additional segments are present;
95 // see http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
96 // send packet
97 Data data = Client.getDefault().getSync(forwarder, interest);
98 if (data == null) {
99 throw new Exception("Failed to retrieve list of faces from the forwarder.");
100 }
101
102 // parse packet
Andrew Brownc46c1602015-02-18 10:45:56 -0800103 return StatusDataset.wireDecode(data.getContent(), FaceStatus.class);
104 }
105
106 /**
107 * Retrieve a list of routing entries from the RIB; calls
108 * /localhost/nfd/rib/list which requires a local Face (all non-local
109 * packets are dropped)
110 *
111 * @param forwarder Only a localhost Face
112 * @return
113 * @throws Exception
114 */
115 public static List<RibEntry> getRouteList(Face forwarder) throws Exception {
116 // build management Interest packet; see http://redmine.named-data.net/projects/nfd/wiki/StatusDataset
117 Interest interest = new Interest(new Name("/localhost/nfd/rib/list"));
118 interest.setMustBeFresh(true);
119 interest.setChildSelector(Interest.CHILD_SELECTOR_RIGHT);
120 interest.setInterestLifetimeMilliseconds(DEFAULT_TIMEOUT);
121
122 // send packet
123 Data data = Client.getDefault().getSync(forwarder, interest);
124 if (data == null) {
125 throw new Exception("Failed to retrieve list of faces from the forwarder.");
126 }
127
128 // parse packet
129 return StatusDataset.wireDecode(data.getContent(), RibEntry.class);
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800130 }
131
132 /**
133 * Helper method to register a new face on the forwarder; as mentioned at
134 * http://named-data.net/doc/NFD/current/manpages/nfdc.html, this is more
135 * for debugging; use 'register' instead
136 *
137 * @param forwarder Only a localhost Face
138 * @param faceId
139 * @param prefix
140 * @return
141 * @throws Exception
142 */
143 public static boolean addNextHop(Face forwarder, int faceId, Name prefix) throws Exception {
144 // build command name
145 Name command = new Name("/localhost/nfd/fib/add-nexthop");
146 ControlParameters parameters = new ControlParameters();
147 parameters.setName(prefix);
148 parameters.setFaceId(faceId);
149 command.append(parameters.wireEncode());
150
151 // send the interest
152 return sendCommandAndErrorCheck(forwarder, new Interest(command));
153 }
154
155 /**
156 * Create a new face on the given forwarder. Ensure the forwarding face is
157 * on the local machine (management requests are to /localhost/...) and that
158 * command signing has been set up (e.g. forwarder.setCommandSigningInfo()).
159 *
160 * @param forwarder Only a localhost Face
161 * @param uri
162 * @return
163 * @throws java.lang.Exception
164 */
165 public static int createFace(Face forwarder, String uri) throws Exception {
166 Name command = new Name("/localhost/nfd/faces/create");
167 ControlParameters parameters = new ControlParameters();
168 parameters.setUri(uri);
169 command.append(parameters.wireEncode());
170
171 // send the interest
172 ControlResponse response = sendCommand(forwarder, new Interest(command));
173
Sebastian Schoenberg9ea45e02015-01-28 10:58:47 -0800174 // check for body and that status code is OK (TODO: 200 should be replaced with a CONSTANT like ControlResponse.STATUS_OK)
Andrew Brownc46c1602015-02-18 10:45:56 -0800175 if (response.getBody().isEmpty() || response.getStatusCode() != 200) {
176 throw new Exception("Failed to create face: " + uri + " " + response.getStatusText());
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800177 }
178
179 // return
Andrew Brownc46c1602015-02-18 10:45:56 -0800180 return response.getBody().get(0).getFaceId();
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800181 }
182
183 /**
184 * Register a route on the forwarder; see
185 * http://named-data.net/doc/NFD/current/manpages/nfdc.html for command-line
186 * usage and http://redmine.named-data.net/projects/nfd/wiki/RibMgmt for
187 * protocol documentation. Ensure the forwarding face is on the local
188 * machine (management requests are to /localhost/...) and that command
189 * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
190 *
191 * @param forwarder Only a localhost Face
192 * @param controlParameters
193 * @return
194 * @throws Exception
195 */
196 public static boolean register(Face forwarder, ControlParameters controlParameters) throws Exception {
197 // build command name
198 Name command = new Name("/localhost/nfd/rib/register");
199 command.append(controlParameters.wireEncode());
200
201 // send the interest
202 return sendCommandAndErrorCheck(forwarder, new Interest(command));
203 }
204
205 /**
206 * Register a route on a forwarder; this will create a new face on the
207 * forwarder to the given URI/route pair. See register(Face,
208 * ControlParameters) for more details documentation.
209 *
210 * @param forwarder Only a localhost Face
211 * @param uri
212 * @param cost
213 * @param route
214 * @return true for successful registration
215 * @throws java.lang.Exception
216 */
217 public static boolean register(Face forwarder, String uri, Name route, int cost) throws Exception {
218 // create the new face
219 int faceId = createFace(forwarder, uri);
220
221 // run base method
222 return register(forwarder, faceId, route, cost);
223 }
224
225 /**
226 * Register a route on a forwarder; this will not create a new face since it
227 * is provided a faceId. See register(Face, ControlParameters) for full
228 * documentation
229 *
230 * @param forwarder Only a localhost Face
231 * @param faceId
232 * @param route
233 * @param cost
234 * @return true for successful registration
235 * @throws java.lang.Exception
236 */
237 public static boolean register(Face forwarder, int faceId, Name route, int cost) throws Exception {
238 // build command name
239 ControlParameters parameters = new ControlParameters();
240 parameters.setName(route);
241 parameters.setFaceId(faceId);
242 parameters.setCost(cost);
243 ForwardingFlags flags = new ForwardingFlags();
244 flags.setCapture(true);
245 flags.setChildInherit(true);
246 parameters.setForwardingFlags(flags);
247
248 // run base method
249 return register(forwarder, parameters);
250 }
251
252 /**
253 * Set a strategy on the forwarder; see
254 * http://named-data.net/doc/NFD/current/manpages/nfdc.html for command-line
255 * usage and http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice
256 * for protocol documentation. Ensure the forwarding face is on the local
257 * machine (management requests are to /localhost/...) and that command
258 * signing has been set up (e.g. forwarder.setCommandSigningInfo()).
259 *
260 * @param forwarder Only a localhost Face
261 * @param prefix
262 * @param strategy
263 * @return true for successful command
264 * @throws Exception
265 */
266 public static boolean setStrategy(Face forwarder, Name prefix, Name strategy) throws Exception {
267 // build command name
268 Name command = new Name("/localhost/nfd/strategy-choice/set");
269 ControlParameters parameters = new ControlParameters();
270 parameters.setName(prefix);
271 parameters.setStrategy(strategy);
272 command.append(parameters.wireEncode());
273
274 // send the interest
275 return sendCommandAndErrorCheck(forwarder, new Interest(command));
276 }
277
278 /**
279 * Send an interest as a command to the forwarder; this method will convert
280 * the interest to a command interest and block until a response is received
281 * from the forwarder. Ensure the forwarding face is on the local machine
282 * (management requests are to /localhost/...) and that command signing has
283 * been set up (e.g. forwarder.setCommandSigningInfo()).
284 *
285 * @param forwarder Only a localhost Face
286 * @param interest As described at
287 * http://redmine.named-data.net/projects/nfd/wiki/ControlCommand, the
288 * requested interest must have encoded ControlParameters appended to the
289 * interest name
290 * @return
291 * @throws net.named_data.jndn.security.SecurityException
292 * @throws java.io.IOException
293 * @throws net.named_data.jndn.encoding.EncodingException
294 */
295 public static ControlResponse sendCommand(Face forwarder, Interest interest) throws SecurityException, IOException, EncodingException {
296 forwarder.makeCommandInterest(interest);
297
298 // send command packet
299 Data data = Client.getDefault().getSync(forwarder, interest);
300 if (data == null) {
301 throw new IOException("Failed to receive command response.");
302 }
303
304 // return response
Andrew Brownc46c1602015-02-18 10:45:56 -0800305 ControlResponse response = new ControlResponse();
306 response.wireDecode(data.getContent().buf());
307 return response;
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800308 }
309
310 /**
311 * Send an interest as a command to the forwarder; this method will convert
312 * the interest to a command interest and block until a response is received
313 * from the forwarder.
314 *
315 * @param forwarder Only a localhost Face
316 * @param interest As described at
317 * http://redmine.named-data.net/projects/nfd/wiki/ControlCommand, the
318 * requested interest must have encoded ControlParameters appended to the
319 * interest name
320 * @return
321 * @throws net.named_data.jndn.security.SecurityException
322 * @throws java.io.IOException
323 * @throws net.named_data.jndn.encoding.EncodingException
324 */
325 public static boolean sendCommandAndErrorCheck(Face forwarder, Interest interest) throws SecurityException, IOException, EncodingException {
326 ControlResponse response = sendCommand(forwarder, interest);
Andrew Brownc46c1602015-02-18 10:45:56 -0800327 if (response.getStatusCode() < 400) {
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800328 return true;
329 } else {
Andrew Brownc46c1602015-02-18 10:45:56 -0800330 logger.warning("Command sent but failed: " + response.getStatusCode() + " " + response.getStatusText());
Andrew Brown2f1fdbf2015-01-21 10:52:29 -0800331 return false;
332 }
333 }
334}