summaryrefslogtreecommitdiffstats
path: root/networking/using_networking_rtems_app.rst
blob: 369d904cd03541e54cf53a6e359a26688e720880 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
.. comment SPDX-License-Identifier: CC-BY-SA-4.0

.. COMMENT: Written by Eric Norum
.. Copyright (C) 1988, 2002 On-Line Applications Research Corporation (OAR)
.. COMMENT: All rights reserved.

Using Networking in an RTEMS Application
########################################

Makefile changes
================

Including the required managers
-------------------------------

The FreeBSD networking code requires several RTEMS managers in the application:

.. code-block:: c

    MANAGERS = io event semaphore

Increasing the size of the heap
-------------------------------

The networking tasks allocate a lot of memory.  For most applications the heap
should be at least 256 kbytes.  The amount of memory set aside for the heap can
be adjusted by setting the ``CFLAGS_LD`` definition as shown below:

.. code-block:: c

    CFLAGS_LD += -Wl,--defsym -Wl,HeapSize=0x80000

This sets aside 512 kbytes of memory for the heap.

System Configuration
====================

The networking tasks allocate some RTEMS objects.  These must be accounted for
in the application configuration table.  The following lists the requirements.

*TASKS*
    One network task plus a receive and transmit task for each device.

*SEMAPHORES*
    One network semaphore plus one syslog mutex semaphore if the application
    uses openlog/syslog.

*EVENTS*
    The network stack uses ``RTEMS_EVENT_24`` and ``RTEMS_EVENT_25``.  This has
    no effect on the application configuration, but application tasks which
    call the network functions should not use these events for other purposes.

Initialization
==============

Additional include files
------------------------

The source file which declares the network configuration structures and calls
the network initialization function must include

.. code-block:: c

    #include <rtems/rtems_bsdnet.h>

Network Configuration
---------------------

The network configuration is specified by declaring and initializing the
``rtems_bsdnet_config`` structure.

.. code-block:: c

    struct rtems_bsdnet_config {
        /*
         * This entry points to the head of the ifconfig chain.
         */
        struct rtems_bsdnet_ifconfig *ifconfig;
        /*
         * This entry should be rtems_bsdnet_do_bootp if BOOTP
         * is being used to configure the network, and NULL
         * if BOOTP is not being used.
         */
        void                          (*bootp)(void);
         /*
          * The remaining items can be initialized to 0, in
          * which case the default value will be used.
          */
        rtems_task_priority           network_task_priority;  /* 100        */
        unsigned long                 mbuf_bytecount;         /* 64 kbytes  */
        unsigned long                 mbuf_cluster_bytecount; /* 128 kbytes */
        char                          *hostname;              /* BOOTP      */
        char                          *domainname;            /* BOOTP      */
        char                          *gateway;               /* BOOTP      */
        char                          *log_host;              /* BOOTP      */
        char                          *name_server[3];        /* BOOTP      */
        char                          *ntp_server[3];         /* BOOTP      */
        unsigned long                 sb_efficiency;          /* 2          */
        /* UDP TX: 9216 bytes */
        unsigned long                 udp_tx_buf_size;
        /* UDP RX: 40 * (1024 + sizeof(struct sockaddr_in)) */
        unsigned long                 udp_rx_buf_size;
        /* TCP TX: 16 * 1024 bytes */
        unsigned long                 tcp_tx_buf_size;
        /* TCP TX: 16 * 1024 bytes */
        unsigned long                 tcp_rx_buf_size;
        /* Default Network Tasks CPU Affinity */
        #ifdef RTEMS_SMP
             const cpu_set_t         *network_task_cpuset;
             size_t                   network_task_cpuset_size;
        #endif
    };

The structure entries are described in the following table.  If your
application uses BOOTP/DHCP to obtain network configuration information and if
you are happy with the default values described below, you need to provide only
the first two entries in this structure.

``struct rtems_bsdnet_ifconfig *ifconfig``
    A pointer to the first configuration structure of the first network device.
    This structure is described in the following section.  You must provide a
    value for this entry since there is no default value for it.

``void (*bootp)(void)``
    This entry should be set to ``rtems_bsdnet_do_bootp`` if your application
    by default uses the BOOTP/DHCP client protocol to obtain network
    configuration information.  It should be set to ``NULL`` if your
    application does not use BOOTP/DHCP.  You can also use
    ``rtems_bsdnet_do_bootp_rootfs`` to have a set of standard files created
    with the information return by the BOOTP/DHCP protocol. The IP address is
    added to :file:`/etc/hosts` with the host name and domain returned. If no
    host name or domain is returned ``me.mydomain`` is used. The BOOTP/DHCP
    server's address is also added to :file:`/etc/hosts`. The domain name
    server listed in the BOOTP/DHCP information are added to
    :file:`/etc/resolv.conf`. A``search`` record is also added if a domain is
    returned. The files are created if they do not exist.  The default
    ``rtems_bsdnet_do_bootp`` and ``rtems_bsdnet_do_bootp_rootfs`` handlers
    will loop for-ever waiting for a BOOTP/DHCP server to respond. If an error
    is detected such as not valid interface or valid hardware address the
    target will reboot allowing any hardware reset to correct itself.  You can
    provide your own custom handler which allows you to perform an
    initialization that meets your specific system requirements. For example
    you could try BOOTP/DHCP then enter a configuration tool if no server is
    found allowing the user to switch to a static configuration.

``int network_task_priority``
    The priority at which the network task and network device
    receive and transmit tasks will run.
    If a value of 0 is specified the tasks will run at priority 100.

``unsigned long mbuf_bytecount``
    The number of bytes to allocate from the heap for use as mbufs.
    If a value of 0 is specified, 64 kbytes will be allocated.

``unsigned long mbuf_cluster_bytecount``
    The number of bytes to allocate from the heap for use as mbuf clusters.
    If a value of 0 is specified, 128 kbytes will be allocated.

``char *hostname``
    The host name of the system.
    If this, or any of the following, entries are ``NULL`` the value
    may be obtained from a BOOTP/DHCP server.

``char *domainname``
    The name of the Internet domain to which the system belongs.

``char *gateway``
    The Internet host number of the network gateway machine, specified in
    'dotted decimal' (``129.128.4.1``) form.

``char *log_host``
    The Internet host number of the machine to which ``syslog`` messages will
    be sent.

``char *name_server[3]``
    The Internet host numbers of up to three machines to be used as Internet
    Domain Name Servers.

``char *ntp_server[3]``
    The Internet host numbers of up to three machines to be used as
    Network Time Protocol (NTP) Servers.

``unsigned long sb_efficiency``
    This is the first of five configuration parameters related to the amount of
    memory each socket may consume for buffers.  The TCP/IP stack reserves
    buffers (e.g. mbufs) for each open socket.  The TCP/IP stack has different
    limits for the transmit and receive buffers associated with each TCP and
    UDP socket.  By tuning these parameters, the application developer can make
    trade-offs between memory consumption and performance.  The default
    parameters favor performance over memory consumption.  See
    http://www.rtems.org/ml/rtems-users/2004/february/msg00200.html for more
    details but note that after the RTEMS 4.8 release series, the
    ``sb_efficiency`` default was changed from ``8`` to ``2``.  The user should
    also be aware of the ``SO_SNDBUF`` and ``SO_RCVBUF`` IO control operations.
    These can be used to specify the send and receive buffer sizes for a
    specific socket.  There is no standard IO control to change the
    ``sb_efficiency`` factor.  The ``sb_efficiency`` parameter is a buffering
    factor used in the implementation of the TCP/IP stack.  The default is
    ``2`` which indicates double buffering.  When allocating memory for each
    socket, this number is multiplied by the buffer sizes for that socket.

``unsigned long udp_tx_buf_size``
    This configuration parameter specifies the maximum amount of buffer memory
    which may be used for UDP sockets to transmit with.  The default size is
    9216 bytes which corresponds to the maximum datagram size.

``unsigned long udp_rx_buf_size``
    This configuration parameter specifies the maximum amount of buffer memory
    which may be used for UDP sockets to receive into.  The default size is the
    following length in bytes:

    .. code-block:: c

        40 * (1024 + sizeof(struct sockaddr_in)

``unsigned long tcp_tx_buf_size``
    This configuration parameter specifies the maximum amount of buffer memory
    which may be used for TCP sockets to transmit with.  The default size is
    sixteen kilobytes.

``unsigned long tcp_rx_buf_size``
    This configuration parameter specifies the maximum amount of buffer memory
    which may be used for TCP sockets to receive into.  The default size is
    sixteen kilobytes.

``const cpu_set_t *network_task_cpuset``
    This configuration parameter specifies the CPU affinity of the network
    task. If set to ``0`` the network task can be scheduled on any CPU. Only
    available in SMP configurations.

``size_t network_task_cpuset_size``
    This configuration parameter specifies the size of the
    ``network_task_cpuset`` used. Only available in SMP configurations.

In addition, the following fields in the ``rtems_bsdnet_ifconfig`` are of
interest.

*int port*
    The I/O port number (ex: 0x240) on which the external Ethernet can be
    accessed.

*int irno*
    The interrupt number of the external Ethernet controller.

*int bpar*
    The address of the shared memory on the external Ethernet controller.

Network device configuration
----------------------------

Network devices are specified and configured by declaring and initializing a
``struct rtems_bsdnet_ifconfig`` structure for each network device.

The structure entries are described in the following table.  An application
which uses a single network interface, gets network configuration information
from a BOOTP/DHCP server, and uses the default values for all driver parameters
needs to initialize only the first two entries in the structure.

``char *name``
    The full name of the network device.  This name consists of the driver name
    and the unit number (e.g. ``"scc1"``).  The ``bsp.h`` include file usually
    defines ``RTEMS_BSP_NETWORK_DRIVER_NAME`` as the name of the primary (or
    only) network driver.

``int (*attach)(struct rtems_bsdnet_ifconfig *conf)``

    The address of the driver ``attach`` function.  The network initialization
    function calls this function to configure the driver and attach it to the
    network stack.  The ``bsp.h`` include file usually defines
    ``RTEMS_BSP_NETWORK_DRIVER_ATTACH`` as the name of the attach function of
    the primary (or only) network driver.

``struct rtems_bsdnet_ifconfig *next``
    A pointer to the network device configuration structure for the next
    network interface, or ``NULL`` if this is the configuration structure of
    the last network interface.

``char *ip_address``
    The Internet address of the device, specified in 'dotted decimal'
    (``129.128.4.2``) form, or ``NULL`` if the device configuration information
    is being obtained from a BOOTP/DHCP server.

``char *ip_netmask``
    The Internet inetwork mask of the device, specified in 'dotted decimal'
    (``255.255.255.0``) form, or ``NULL`` if the device configuration
    information is being obtained from a BOOTP/DHCP server.

``void *hardware_address``
    The hardware address of the device, or ``NULL`` if the driver is to obtain
    the hardware address in some other way (usually by reading it from the
    device or from the bootstrap ROM).

``int ignore_broadcast``
    Zero if the device is to accept broadcast packets, non-zero if the device
    is to ignore broadcast packets.

``int mtu``
    The maximum transmission unit of the device, or zero if the driver is to
    choose a default value (typically 1500 for Ethernet devices).

``int rbuf_count``
    The number of receive buffers to use, or zero if the driver is to choose a
    default value

``int xbuf_count``
    The number of transmit buffers to use, or zero if the driver is to choose a
    default value Keep in mind that some network devices may use 4 or more
    transmit descriptors for a single transmit buffer.

A complete network configuration specification can be as simple as the one
shown in the following example.  This configuration uses a single network
interface, gets network configuration information from a BOOTP/DHCP server, and
uses the default values for all driver parameters.

.. code-block:: c

    static struct rtems_bsdnet_ifconfig netdriver_config = {
        RTEMS_BSP_NETWORK_DRIVER_NAME,
        RTEMS_BSP_NETWORK_DRIVER_ATTACH
    };
    struct rtems_bsdnet_config rtems_bsdnet_config = {
        &netdriver_config,
        rtems_bsdnet_do_bootp,
    };

Network initialization
----------------------

The networking tasks must be started before any network I/O operations can be
performed. This is done by calling:

.. code-block:: c

    rtems_bsdnet_initialize_network ();

This function is declared in ``rtems/rtems_bsdnet.h``.  t returns 0 on success
and -1 on failure with an error code in ``errno``.  It is not possible to undo
the effects of a partial initialization, though, so the function can be called
only once irregardless of the return code.  Consequently, if the condition for
the failure can be corrected, the system must be reset to permit another
network initialization attempt.

Application Programming Interface
=================================

The RTEMS network package provides almost a complete set of BSD network
services.  The network functions work like their BSD counterparts with the
following exceptions:

- A given socket can be read or written by only one task at a time.

- The ``select`` function only works for file descriptors associated with
  sockets.

- You must call ``openlog`` before calling any of the ``syslog`` functions.

- *Some of the network functions are not thread-safe.* For example the
  following functions return a pointer to a static buffer which remains valid
  only until the next call:

  ``gethostbyaddr``
  ``gethostbyname``
  ``inet_ntoa`` (``inet_ntop`` is thread-safe, though).

- The RTEMS network package gathers statistics.

- Addition of a mechanism to "tap onto" an interface and monitor every packet
  received and transmitted.

- Addition of ``SO_SNDWAKEUP`` and ``SO_RCVWAKEUP`` socket options.

Some of the new features are discussed in more detail in the following
sections.

Network Statistics
------------------

There are a number of functions to print statistics gathered by the network
stack.  These function are declared in ``rtems/rtems_bsdnet.h``.

``rtems_bsdnet_show_if_stats``
    Display statistics gathered by network interfaces.

``rtems_bsdnet_show_ip_stats``
    Display IP packet statistics.

``rtems_bsdnet_show_icmp_stats``
    Display ICMP packet statistics.

``rtems_bsdnet_show_tcp_stats``
    Display TCP packet statistics.

``rtems_bsdnet_show_udp_stats``
    Display UDP packet statistics.

``rtems_bsdnet_show_mbuf_stats``
    Display mbuf statistics.

``rtems_bsdnet_show_inet_routes``
    Display the routing table.

Tapping Into an Interface
-------------------------

RTEMS add two new ioctls to the BSD networking code, ``SIOCSIFTAP`` and
``SIOCGIFTAP``.  These may be used to set and get a *tap function*.  The tap
function will be called for every Ethernet packet received by the interface.

These are called like other interface ioctls, such as ``SIOCSIFADDR``.  When
setting the tap function with ``SIOCSIFTAP``, set the ifr_tap field of the
ifreq struct to the tap function.  When retrieving the tap function with
``SIOCGIFTAP``, the current tap function will be returned in the ifr_tap field.
To stop tapping packets, call ``SIOCSIFTAP`` with a ``ifr_tap`` field of ``0``.

The tap function is called like this:

.. code-block:: c

    int tap (struct ifnet *, struct ether_header *, struct mbuf *)

The tap function should return ``1`` if the packet was fully handled, in which
case the caller will simply discard the mbuf.  The tap function should return
``0`` if the packet should be passed up to the higher networking layers.

The tap function is called with the network semaphore locked.  It must not make
any calls on the application levels of the networking level itself.  It is safe
to call other non-networking RTEMS functions.

Socket Options
--------------

RTEMS adds two new ``SOL_SOCKET`` level options for ``setsockopt`` and
``getsockopt``: ``SO_SNDWAKEUP`` and ``SO_RCVWAKEUP``.  For both, the option
value should point to a sockwakeup structure.  The sockwakeup structure has the
following fields:

.. code-block:: c

    void    (*sw_pfn) (struct socket *, caddr_t);
    caddr_t sw_arg;

These options are used to set a callback function to be called when, for
example, there is data available from the socket (``SO_RCVWAKEUP``) and when
there is space available to accept data written to the socket
(``SO_SNDWAKEUP``).

If ``setsockopt`` is called with the ``SO_RCVWAKEUP`` option, and the
``sw_pfn`` field is not zero, then when there is data available to be read from
the socket, the function pointed to by the ``sw_pfn`` field will be called.  A
pointer to the socket structure will be passed as the first argument to the
function.  The ``sw_arg`` field set by the ``SO_RCVWAKEUP`` call will be passed
as the second argument to the function.

If ``setsockopt`` is called with the ``SO_SNDWAKEUP`` function, and the
``sw_pfn`` field is not zero, then when there is space available to accept data
written to the socket, the function pointed to by the ``sw_pfn`` field will be
called.  The arguments passed to the function will be as with ``SO_SNDWAKEUP``.

When the function is called, the network semaphore will be locked and the
callback function runs in the context of the networking task.  The function
must be careful not to call any networking functions.  It is OK to call an
RTEMS function; for example, it is OK to send an RTEMS event.

The purpose of these callback functions is to permit a more efficient
alternative to the select call when dealing with a large number of sockets.

The callbacks are called by the same criteria that the select function uses for
indicating "ready" sockets. In Stevens *Unix Network Programming* on page
153-154 in the section "Under what Conditions Is a Descriptor Ready?" you will
find the definitive list of conditions for readable and writable that also
determine when the functions are called.

When the number of received bytes equals or exceeds the socket receive buffer
"low water mark" (default 1 byte) you get a readable callback. If there are 100
bytes in the receive buffer and you only read 1, you will not immediately get
another callback. However, you will get another callback after you read the
remaining 99 bytes and at least 1 more byte arrives. Using a non-blocking
socket you should probably read until it produces error ``EWOULDBLOCK`` and
then allow the readable callback to tell you when more data has arrived.
(Condition 1.a.)

For sending, when the socket is connected and the free space becomes at or
above the "low water mark" for the send buffer (default 4096 bytes) you will
receive a writable callback. You don't get continuous callbacks if you don't
write anything. Using a non-blocking write socket, you can then call write
until it returns a value less than the amount of data requested to be sent or
it produces error ``EWOULDBLOCK`` (indicating buffer full and no longer
writable). When this happens you can try the write again, but it is often
better to go do other things and let the writable callback tell you when space
is available to send again. You only get a writable callback when the free
space transitions to above the "low water mark" and not every time you write to
a non-full send buffer. (Condition 2.a.)

The remaining conditions enumerated by Stevens handle the fact that sockets
become readable and/or writable when connects, disconnects and errors occur,
not just when data is received or sent. For example, when a server "listening"
socket becomes readable it indicates that a client has connected and accept can
be called without blocking, not that network data was received (Condition 1.c).

Adding an IP Alias
------------------

The following code snippet adds an IP alias:

.. code-block:: c

    void addAlias(const char *pName, const char *pAddr, const char *pMask)
    {
        struct ifaliasreq   aliasreq;
        struct sockaddr_in *in;

        /* initialize alias request */
        memset(&aliasreq, 0, sizeof(aliasreq));
        sprintf(aliasreq.ifra_name, pName);

        /* initialize alias address */
        in = (struct sockaddr_in *)&aliasreq.ifra_addr;
        in->sin_family = AF_INET;
        in->sin_len    = sizeof(aliasreq.ifra_addr);
        in->sin_addr.s_addr = inet_addr(pAddr);

        /* initialize alias mask */
        in = (struct sockaddr_in *)&aliasreq.ifra_mask;
        in->sin_family = AF_INET;
        in->sin_len    = sizeof(aliasreq.ifra_mask);
        in->sin_addr.s_addr = inet_addr(pMask);

        /* call to setup the alias */
        rtems_bsdnet_ifconfig(pName, SIOCAIFADDR, &aliasreq);
    }

Thanks to Mike Seirs <mailto:mikes@poliac.com> for this example code.

Adding a Default Route
----------------------

The function provided in this section is functionally equivalent to the command
``route add default gw yyy.yyy.yyy.yyy``:

.. code-block:: c

    void mon_ifconfig(int argc, char *argv[],  unsigned32 command_arg, bool verbose)
    {
        struct sockaddr_in  ipaddr;
        struct sockaddr_in  dstaddr;
        struct sockaddr_in  netmask;
        struct sockaddr_in  broadcast;
        char               *iface;
        int                 f_ip        = 0;
        int                 f_ptp       = 0;
        int                 f_netmask   = 0;
        int                 f_up        = 0;
        int                 f_down      = 0;
        int                 f_bcast     = 0;
        int                 cur_idx;
        int                 rc;
        int                 flags;

        bzero((void*) &ipaddr, sizeof(ipaddr));
        bzero((void*) &dstaddr, sizeof(dstaddr));
        bzero((void*) &netmask, sizeof(netmask));
        bzero((void*) &broadcast, sizeof(broadcast));
        ipaddr.sin_len = sizeof(ipaddr);
        ipaddr.sin_family = AF_INET;
        dstaddr.sin_len = sizeof(dstaddr);
        dstaddr.sin_family = AF_INET;
        netmask.sin_len = sizeof(netmask);
        netmask.sin_family = AF_INET;
        broadcast.sin_len = sizeof(broadcast);
        broadcast.sin_family = AF_INET;
        cur_idx = 0;

        if (argc <= 1) {
            /* display all interfaces */
            iface = NULL;
            cur_idx += 1;
        } else {
            iface = argv[1];
            if (isdigit(*argv[2])) {
                if (inet_pton(AF_INET, argv[2], &ipaddr.sin_addr) < 0) {
                    printf("bad ip address: %s\n", argv[2]);
                    return;
                }
                f_ip = 1;
                cur_idx += 3;
            } else {
                cur_idx += 2;
            }
        }

        if ((f_down !=0) && (f_ip != 0)) {
            f_up = 1;
        }

        while(argc > cur_idx) {
            if (strcmp(argv[cur_idx], "up") == 0) {
                f_up = 1;
                if (f_down != 0) {
                    printf("Can't make interface up and down\n");
                }
            } else if(strcmp(argv[cur_idx], "down") == 0) {
                f_down = 1;
                if (f_up != 0) {
                    printf("Can't make interface up and down\n");
                }
            } else if(strcmp(argv[cur_idx], "netmask") == 0) {
                if ((cur_idx + 1) >= argc) {
                    printf("No netmask address\n");
                    return;
                }
                if (inet_pton(AF_INET, argv[cur_idx+1], &netmask.sin_addr) < 0) {
                    printf("bad netmask: %s\n", argv[cur_idx]);
                    return;
                }
                f_netmask = 1;
                cur_idx += 1;
            } else if(strcmp(argv[cur_idx], "broadcast") == 0) {
                if ((cur_idx + 1) >= argc) {
                    printf("No broadcast address\n");
                    return;
                }
                if (inet_pton(AF_INET, argv[cur_idx+1], &broadcast.sin_addr) < 0) {
                    printf("bad broadcast: %s\n", argv[cur_idx]);
                    return;
                }
               f_bcast = 1;
               cur_idx += 1;
            } else if(strcmp(argv[cur_idx], "pointopoint") == 0) {
                if ((cur_idx + 1) >= argc) {
                    printf("No pointopoint address\n");
                    return;
                }
                if (inet_pton(AF_INET, argv[cur_idx+1], &dstaddr.sin_addr) < 0) {
                    printf("bad pointopoint: %s\n", argv[cur_idx]);
                    return;
                }
                f_ptp = 1;
                cur_idx += 1;
            } else {
                printf("Bad parameter: %s\n", argv[cur_idx]);
                return;
            }
            cur_idx += 1;
        }

        printf("ifconfig ");

        if (iface != NULL) {
            printf("%s ", iface);
            if (f_ip != 0) {
                char str[256];
                inet_ntop(AF_INET, &ipaddr.sin_addr, str, 256);
                printf("%s ", str);
            }
            if (f_netmask != 0) {
                char str[256];
                inet_ntop(AF_INET, &netmask.sin_addr, str, 256);
                printf("netmask %s ", str);
            }
            if (f_bcast != 0) {
                char str[256];
                inet_ntop(AF_INET, &broadcast.sin_addr, str, 256);
                printf("broadcast %s ", str);
            }
            if (f_ptp != 0) {
                char str[256];
                inet_ntop(AF_INET, &dstaddr.sin_addr, str, 256);
                printf("pointopoint %s ", str);
            }
            if (f_up != 0) {
                printf("up\n");
            } else if (f_down != 0) {
                printf("down\n");
            } else {
                printf("\n");
            }
        }

        if ((iface == NULL) || ((f_ip == 0) && (f_down == 0) && (f_up == 0))) {
            rtems_bsdnet_show_if_stats();
            return;
        }

        flags = 0;
        if (f_netmask) {
            rc = rtems_bsdnet_ifconfig(iface, SIOCSIFNETMASK, &netmask);
            if (rc < 0) {
                printf("Could not set netmask: %s\n", strerror(errno));
                return;
            }
        }
        if (f_bcast) {
            rc = rtems_bsdnet_ifconfig(iface, SIOCSIFBRDADDR, &broadcast);
            if (rc < 0) {
                printf("Could not set broadcast: %s\n", strerror(errno));
                return;
            }
        }
        if (f_ptp) {
            rc = rtems_bsdnet_ifconfig(iface, SIOCSIFDSTADDR, &dstaddr);
            if (rc < 0) {
                printf("Could not set destination address: %s\n", strerror(errno));
                return;
            }
            flags |= IFF_POINTOPOINT;
        }

        /* This must come _after_ setting the netmask, broadcast addresses */
        if (f_ip) {
            rc = rtems_bsdnet_ifconfig(iface, SIOCSIFADDR, &ipaddr);
            if (rc < 0) {
                printf("Could not set IP address: %s\n", strerror(errno));
                return;
            }
        }
        if (f_up != 0) {
            flags |= IFF_UP;
        }
        if (f_down != 0) {
            printf("Warning: taking interfaces down is not supported\n");
        }

        rc = rtems_bsdnet_ifconfig(iface, SIOCSIFFLAGS, &flags);
        if (rc < 0) {
            printf("Could not set interface flags: %s\n", strerror(errno));
            return;
        }
    }

    void mon_route(int argc, char *argv[],  unsigned32 command_arg, bool verbose)
    {
        int                cmd;
        struct sockaddr_in dst;
        struct sockaddr_in gw;
        struct sockaddr_in netmask;
        int                f_host;
        int                f_gw       = 0;
        int                cur_idx;
        int                flags;
        int                rc;

        memset(&dst, 0, sizeof(dst));
        memset(&gw, 0, sizeof(gw));
        memset(&netmask, 0, sizeof(netmask));
        dst.sin_len = sizeof(dst);
        dst.sin_family = AF_INET;
        dst.sin_addr.s_addr = inet_addr("0.0.0.0");
        gw.sin_len = sizeof(gw);
        gw.sin_family = AF_INET;
        gw.sin_addr.s_addr = inet_addr("0.0.0.0");
        netmask.sin_len = sizeof(netmask);
        netmask.sin_family = AF_INET;
        netmask.sin_addr.s_addr = inet_addr("255.255.255.0");

        if (argc < 2) {
            rtems_bsdnet_show_inet_routes();
            return;
        }

        if (strcmp(argv[1], "add") == 0) {
            cmd = RTM_ADD;
        } else if (strcmp(argv[1], "del") == 0) {
            cmd = RTM_DELETE;
        } else {
            printf("invalid command: %s\n", argv[1]);
            printf("\tit should be 'add' or 'del'\n");
            return;
        }

        if (argc < 3) {
            printf("not enough arguments\n");
            return;
        }

        if (strcmp(argv[2], "-host") == 0) {
            f_host = 1;
        } else if (strcmp(argv[2], "-net") == 0) {
            f_host = 0;
        } else {
            printf("Invalid type: %s\n", argv[1]);
            printf("\tit should be '-host' or '-net'\n");
            return;
        }

        if (argc < 4) {
            printf("not enough arguments\n");
            return;
        }

        inet_pton(AF_INET, argv[3], &dst.sin_addr);

        cur_idx = 4;
        while(cur_idx < argc) {
            if (strcmp(argv[cur_idx], "gw") == 0) {
                if ((cur_idx +1) >= argc) {
                    printf("no gateway address\n");
                    return;
                }
                f_gw = 1;
                inet_pton(AF_INET, argv[cur_idx + 1], &gw.sin_addr);
                cur_idx += 1;
            } else if(strcmp(argv[cur_idx], "netmask") == 0) {
                if ((cur_idx +1) >= argc) {
                    printf("no netmask address\n");
                    return;
                }
                f_gw = 1;
                inet_pton(AF_INET, argv[cur_idx + 1], &netmask.sin_addr);
                cur_idx += 1;
            } else {
                printf("Unknown argument\n");
                return;
            }
            cur_idx += 1;
        }

        flags = RTF_STATIC;
        if (f_gw != 0) {
            flags |= RTF_GATEWAY;
        }
        if (f_host != 0) {
            flags |= RTF_HOST;
        }

        rc = rtems_bsdnet_rtrequest(cmd, &dst, &gw, &netmask, flags, NULL);
        if (rc < 0) {
            printf("Error adding route\n");
        }
    }

Thanks to Jay Monkman <mailto:jtm@smoothmsmoothie.com> for this example
code.

Time Synchronization Using NTP
------------------------------

.. code-block:: c

    int rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority);

If the interval argument is ``0`` the routine synchronizes the RTEMS
time-of-day clock with the first NTP server in the ``rtems_bsdnet_ntpserve``
array and returns.  The priority argument is ignored.

If the interval argument is greater than 0, the routine also starts an RTEMS
task at the specified priority and polls the NTP server every 'interval'
seconds.  NOTE: This mode of operation has not yet been implemented.

On successful synchronization of the RTEMS time-of-day clock the routine
returns ``0``.  If an error occurs a message is printed and the routine returns
``-1`` with an error code in errno.  There is no timeout - if there is no
response from an NTP server the routine will wait forever.