1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Display the link status properties of a network interface.
25 *
26 * To build:
27 * cc monitorlink.c -o monitorlink -framework IOKit -Wall
28 */
29
30#include <assert.h>
31
32#include <CoreFoundation/CoreFoundation.h>
33#include <IOKit/IOCFSerialize.h>
34#include <IOKit/IOKitLib.h>
35
36#include <IOKit/network/IONetworkLib.h>
37
38#include <ctype.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <unistd.h>
42
43#include <mach/mach.h>
44
45//---------------------------------------------------------------------------
46// Property table keys.
47
48#define IF_NAME_KEY              "BSD Name"
49#define kIOMediumDictionary      "IOMediumDictionary"
50#define kIODefaultMedium         "IODefaultMedium"
51#define kIOCurrentMedium         "IOCurrentMedium"
52#define kIOActiveMedium          "IOActiveMedium"
53#define kIOLinkSpeed             "IOLinkSpeed"
54#define kIOLinkStatus            "IOLinkStatus"
55#define kIOLinkData              "IOLinkData"
56
57//---------------------------------------------------------------------------
58// Utilities functions to print CFNumber and CFString.
59
60static void CFNumberShow(CFNumberRef object)
61{
62    long long number = 0;
63
64    if (CFNumberGetValue(object, kCFNumberLongLongType, &number))
65    {
66        printf("%qd", number);
67    }
68}
69
70static void CFStringShow(CFStringRef object)
71{
72    const char * c = CFStringGetCStringPtr(object,
73                                           kCFStringEncodingMacRoman);
74
75    if (c)
76        printf(c);
77    else
78    {
79        CFIndex bufferSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(object),
80		kCFStringEncodingMacRoman) + sizeof('\0');
81        char *  buffer     = (char *) malloc(bufferSize);
82
83        if (buffer)
84        {
85            if ( CFStringGetCString(
86                                /* string     */ object,
87                                /* buffer     */ buffer,
88                                /* bufferSize */ bufferSize,
89                                /* encoding   */ kCFStringEncodingMacRoman) )
90                printf(buffer);
91
92            free(buffer);
93        }
94    }
95}
96
97//---------------------------------------------------------------------------
98// Returns non zero if the property table of the object has a "BSD Name"
99// string entry, and the string matches the provided string argument.
100
101static int matchBSDNameProperty(io_object_t obj, CFStringRef ifname)
102{
103    int                     match = 0;
104    kern_return_t           kr;
105    CFMutableDictionaryRef  properties;
106    CFStringRef             string;
107
108    kr = IORegistryEntryCreateCFProperties(obj,
109                                           &properties,
110                                           kCFAllocatorDefault,
111                                           kNilOptions);
112
113    if ((kr != KERN_SUCCESS) || !properties) {
114        printf("IORegistryEntryCreateCFProperties error %x\n", kr);
115        return false;
116    }
117
118    // Look up the interface "BSD Name" property and perform matching.
119    //
120    string = (CFStringRef) CFDictionaryGetValue(properties,
121                                                CFSTR(IF_NAME_KEY));
122    if (string) {
123        if (CFStringCompare(ifname, string, kNilOptions) == kCFCompareEqualTo)
124        {
125            printf("[ ");
126            CFStringShow(string);
127            printf(" ]\n");
128            match = 1;
129        }
130    }
131
132    CFRelease(properties);
133
134    return match;   // true if matched
135}
136
137//---------------------------------------------------------------------------
138// Searches the IOKit registry and return an interface object with the
139// given BSD interface name, i.e. en0.
140
141static io_object_t
142getInterfaceWithBSDName(mach_port_t masterPort, CFStringRef ifname)
143{
144    kern_return_t   kr;
145    io_iterator_t   ite;
146    io_object_t     obj = 0;
147    const char *    className = "IONetworkInterface";
148
149    kr = IORegistryCreateIterator(masterPort,
150                                  kIOServicePlane,
151                                  true,             /* recursive */
152                                  &ite);
153
154    if (kr != kIOReturnSuccess) {
155        printf("IORegistryCreateIterator() error %x\n", kr);
156        return 0;
157    }
158
159    while ((obj = IOIteratorNext(ite))) {
160        if (IOObjectConformsTo(obj, (char *) className) &&
161            (matchBSDNameProperty(obj, ifname)))
162            break;
163
164        IOObjectRelease(obj);
165        obj = 0;
166    }
167
168    IORegistryDisposeEnumerator(ite);
169
170    return obj;
171}
172
173//---------------------------------------------------------------------------
174// Display the link related properties for an IONetworkController object.
175
176static void
177printLinkProperties(io_object_t controller)
178{
179    kern_return_t           kr;
180    CFMutableDictionaryRef  properties = 0;
181    CFStringRef             string;
182    CFNumberRef             number;
183
184    do {
185        kr = IORegistryEntryCreateCFProperties(controller,
186                                               &properties,
187                                               kCFAllocatorDefault,
188                                               kNilOptions);
189        if (kr != kIOReturnSuccess) {
190            printf("Error: cannot get properties %x\n", kr);
191            break;
192        }
193
194        // Print kIOActiveMedium string.
195        //
196        string = (CFStringRef) CFDictionaryGetValue(properties,
197                                                    CFSTR(kIOActiveMedium));
198        printf("Active  medium : ");
199        if (string) {
200            CFStringShow(string);
201            printf("\n");
202        }
203        else {
204            printf("None\n");
205        }
206
207        // Print kIOCurrentMedium string.
208        //
209        string = (CFStringRef) CFDictionaryGetValue(properties,
210                                                    CFSTR(kIOCurrentMedium));
211        printf("Current medium : ");
212        if (string) {
213            CFStringShow(string);
214            printf("\n");
215        }
216        else {
217            printf("None\n");
218        }
219
220        // Print kIOLinkSpeed number.
221        //
222        number = (CFNumberRef) CFDictionaryGetValue(properties,
223                                                    CFSTR(kIOLinkSpeed));
224        if (number) {
225            printf("Link speed bps : ");
226            CFNumberShow(number);
227            printf("\n");
228        }
229
230        // Print kIOLinkSpeed number.
231        //
232        number = (CFNumberRef) CFDictionaryGetValue(properties,
233                                                    CFSTR(kIOLinkStatus));
234        if (number) {
235            long status;
236
237            if (CFNumberGetValue(number, kCFNumberLongType, &status))
238            {
239                printf("Link status    : ");
240
241                if (status & kIONetworkLinkValid)
242                {
243                    printf("%s\n", (status & kIONetworkLinkActive) ? "Active" :
244                        "Inactive");
245                }
246                else
247                    printf("Not reported\n");
248            }
249        }
250    }
251    while (0);
252
253    if (properties)
254        CFRelease(properties);
255}
256
257//---------------------------------------------------------------------------
258// Get the parent object (a network controller) of an interface object.
259
260static io_object_t
261getControllerForInterface(io_object_t netif)
262{
263    io_iterator_t    ite;
264    kern_return_t    kr;
265    io_object_t      controller = 0;
266
267    // We have the interface, we need its parent, the network controller.
268
269    kr = IORegistryEntryGetParentIterator(netif, kIOServicePlane, &ite);
270    if (kr == kIOReturnSuccess) {
271        controller = IOIteratorNext(ite);   // the first entry
272        IORegistryDisposeEnumerator(ite);
273    }
274
275    return controller;  // caller must release this object
276}
277
278//---------------------------------------------------------------------------
279// Returns when a mach message is received on the port specified.
280
281static void
282waitForNotification(mach_port_t port)
283{
284    kern_return_t   kr;
285
286    struct {
287        IONetworkNotifyMsg      hdr;
288        mach_msg_trailer_t      trailer;
289    } msg;
290
291    // Now wait for a notification.
292    //
293    kr = mach_msg(&msg.hdr.h, MACH_RCV_MSG,
294                  0, sizeof(msg), port, 0, MACH_PORT_NULL);
295
296    if (kr != KERN_SUCCESS)
297        printf("Error: mach_msg %x\n", kr);
298//  else
299//      printf("\n\n[message id=%x]\n", msg.hdr.h.msgh_id);
300}
301
302//---------------------------------------------------------------------------
303// Display usage and quit.
304
305static void usage(void)
306{
307    /*
308     * Print usage and exit.
309     */
310    printf("usage: monitorlink [-i ifname] [-w]\n");
311    printf("option flags:\n");
312    printf("   -i ifname : specify interface. Default is en0.\n");
313    printf("   -w        : wait for link event notification.\n");
314    printf("\n");
315    exit(1);
316}
317
318//---------------------------------------------------------------------------
319// Main function.
320
321int
322main(int argc, char ** argv)
323{
324    mach_port_t     masterPort;
325    mach_port_t     notifyPort;
326    kern_return_t   kr;
327    io_object_t     netif;
328    io_object_t     controller;
329    io_connect_t    con;
330    int             ch;
331    const char *    name = "en0";   // default interface name
332    CFStringRef     ifname;
333    int             wait = 0;
334
335    while ((ch = getopt(argc, argv, ":i:w")) != -1)
336    {
337        switch ((char) ch)
338        {
339            case 'i':   /* specify interface */
340                if (!optarg)
341                    usage();
342                name = optarg;
343                break;
344
345            case 'w':   /* specify interface */
346                wait = 1;
347                break;
348
349            default:
350                usage();
351        }
352    }
353
354    if (name == 0)
355        usage();
356
357    // Get master device port
358    //
359    kr = IOMasterPort(bootstrap_port, &masterPort);
360    if (kr != KERN_SUCCESS)
361        printf("IOMasterPort() failed: %x\n", kr);
362
363
364    // Create a mach port to receive media/link change notifications.
365    //
366    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
367                            &notifyPort);
368    if (kr != KERN_SUCCESS) {
369        printf("Error: mach_port_allocate %x\n", kr);
370        exit(1);
371    }
372
373    ifname = CFStringCreateWithCString(kCFAllocatorDefault,
374                                       name,
375                                       kCFStringEncodingMacRoman);
376
377    netif = getInterfaceWithBSDName(masterPort, ifname);
378
379    if (netif)
380    {
381        kr = IONetworkOpen(netif, &con);
382        if (kr != kIOReturnSuccess)
383        {
384            printf("Error: IONetworkOpen error %x\n", kr);
385            exit(1);
386        }
387
388        kr = IOConnectSetNotificationPort(con,
389             kIONUCNotificationTypeLinkChange, notifyPort, 0);
390        if (kr != kIOReturnSuccess) {
391            printf("Error: IOConnectSetNotificationPort %x\n", kr);
392            exit(1);
393        }
394
395        controller = getControllerForInterface(netif);
396
397        if (controller)
398        {
399            do {
400                printLinkProperties(controller);
401                printf("\n");
402
403                if (wait)
404                    waitForNotification(notifyPort);
405            }
406            while (wait);
407
408            IOObjectRelease(controller);
409        }
410
411        IONetworkClose(con);
412        IOObjectRelease(netif);
413    }
414
415    exit(0);
416    return 0;
417}
418