VirtualBox

source: vbox/trunk/src/VBox/Main/NetworkAdapterImpl.cpp@ 28322

Last change on this file since 28322 was 28295, checked in by vboxsync, 15 years ago

Main: saving NAT Engine states while network switches.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 KB
Line 
1/* $Id: NetworkAdapterImpl.cpp 28295 2010-04-14 11:51:07Z vboxsync $ */
2/** @file
3 * Implementation of INetworkAdaptor in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "NetworkAdapterImpl.h"
23#include "NATEngineImpl.h"
24#include "AutoCaller.h"
25#include "Logging.h"
26#include "MachineImpl.h"
27#include "GuestOSTypeImpl.h"
28
29#include <iprt/string.h>
30#include <iprt/cpp/utils.h>
31
32#include <VBox/err.h>
33#include <VBox/settings.h>
34
35#include "AutoStateDep.h"
36
37// constructor / destructor
38////////////////////////////////////////////////////////////////////////////////
39
40NetworkAdapter::NetworkAdapter()
41 : mParent(NULL)
42{
43}
44
45NetworkAdapter::~NetworkAdapter()
46{
47}
48
49HRESULT NetworkAdapter::FinalConstruct()
50{
51
52 return S_OK;
53}
54
55void NetworkAdapter::FinalRelease()
56{
57 uninit();
58}
59
60// public initializer/uninitializer for internal purposes only
61////////////////////////////////////////////////////////////////////////////////
62
63/**
64 * Initializes the network adapter object.
65 *
66 * @param aParent Handle of the parent object.
67 */
68HRESULT NetworkAdapter::init(Machine *aParent, ULONG aSlot)
69{
70 LogFlowThisFunc(("aParent=%p, aSlot=%d\n", aParent, aSlot));
71
72 ComAssertRet(aParent, E_INVALIDARG);
73 ComAssertRet(aSlot < SchemaDefs::NetworkAdapterCount, E_INVALIDARG);
74
75 /* Enclose the state transition NotReady->InInit->Ready */
76 AutoInitSpan autoInitSpan(this);
77 AssertReturn(autoInitSpan.isOk(), E_FAIL);
78
79 unconst(mParent) = aParent;
80 unconst(mNATEngine).createObject();
81 mNATEngine->init(aParent);
82 /* mPeer is left null */
83
84 m_fModified = false;
85
86 mData.allocate();
87
88 /* initialize data */
89 mData->mSlot = aSlot;
90
91 /* default to Am79C973 */
92 mData->mAdapterType = NetworkAdapterType_Am79C973;
93
94 /* generate the MAC address early to guarantee it is the same both after
95 * changing some other property (i.e. after mData.backup()) and after the
96 * subsequent mData.rollback(). */
97 generateMACAddress();
98
99 /* Confirm a successful initialization */
100 autoInitSpan.setSucceeded();
101
102 return S_OK;
103}
104
105/**
106 * Initializes the network adapter object given another network adapter object
107 * (a kind of copy constructor). This object shares data with
108 * the object passed as an argument.
109 *
110 * @note This object must be destroyed before the original object
111 * it shares data with is destroyed.
112 *
113 * @note Locks @a aThat object for reading.
114 */
115HRESULT NetworkAdapter::init(Machine *aParent, NetworkAdapter *aThat)
116{
117 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
118
119 ComAssertRet(aParent && aThat, E_INVALIDARG);
120
121 /* Enclose the state transition NotReady->InInit->Ready */
122 AutoInitSpan autoInitSpan(this);
123 AssertReturn(autoInitSpan.isOk(), E_FAIL);
124
125 unconst(mParent) = aParent;
126 unconst(mPeer) = aThat;
127 unconst(mNATEngine).createObject();
128 mNATEngine->init(aParent, aThat->mNATEngine);
129
130 AutoCaller thatCaller (aThat);
131 AssertComRCReturnRC(thatCaller.rc());
132
133 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
134 mData.share (aThat->mData);
135
136 /* Confirm a successful initialization */
137 autoInitSpan.setSucceeded();
138
139 return S_OK;
140}
141
142/**
143 * Initializes the guest object given another guest object
144 * (a kind of copy constructor). This object makes a private copy of data
145 * of the original object passed as an argument.
146 *
147 * @note Locks @a aThat object for reading.
148 */
149HRESULT NetworkAdapter::initCopy(Machine *aParent, NetworkAdapter *aThat)
150{
151 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
152
153 ComAssertRet(aParent && aThat, E_INVALIDARG);
154
155 /* Enclose the state transition NotReady->InInit->Ready */
156 AutoInitSpan autoInitSpan(this);
157 AssertReturn(autoInitSpan.isOk(), E_FAIL);
158
159 unconst(mParent) = aParent;
160 /* mPeer is left null */
161
162 unconst(mNATEngine).createObject();
163 mNATEngine->initCopy(aParent, aThat->mNATEngine);
164
165 AutoCaller thatCaller (aThat);
166 AssertComRCReturnRC(thatCaller.rc());
167
168 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
169 mData.attachCopy (aThat->mData);
170
171 /* Confirm a successful initialization */
172 autoInitSpan.setSucceeded();
173
174 return S_OK;
175}
176
177/**
178 * Uninitializes the instance and sets the ready flag to FALSE.
179 * Called either from FinalRelease() or by the parent when it gets destroyed.
180 */
181void NetworkAdapter::uninit()
182{
183 LogFlowThisFunc(("\n"));
184
185 /* Enclose the state transition Ready->InUninit->NotReady */
186 AutoUninitSpan autoUninitSpan(this);
187 if (autoUninitSpan.uninitDone())
188 return;
189
190 mData.free();
191
192 unconst(mNATEngine).setNull();
193 unconst(mPeer) = NULL;
194 unconst(mParent) = NULL;
195}
196
197// INetworkAdapter properties
198////////////////////////////////////////////////////////////////////////////////
199
200STDMETHODIMP NetworkAdapter::COMGETTER(AdapterType) (NetworkAdapterType_T *aAdapterType)
201{
202 CheckComArgOutPointerValid(aAdapterType);
203
204 AutoCaller autoCaller(this);
205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
206
207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
208
209 *aAdapterType = mData->mAdapterType;
210
211 return S_OK;
212}
213
214STDMETHODIMP NetworkAdapter::COMSETTER(AdapterType) (NetworkAdapterType_T aAdapterType)
215{
216 AutoCaller autoCaller(this);
217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
218
219 /* the machine needs to be mutable */
220 AutoMutableStateDependency adep(mParent);
221 if (FAILED(adep.rc())) return adep.rc();
222
223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
224
225 /* make sure the value is allowed */
226 switch (aAdapterType)
227 {
228 case NetworkAdapterType_Am79C970A:
229 case NetworkAdapterType_Am79C973:
230#ifdef VBOX_WITH_E1000
231 case NetworkAdapterType_I82540EM:
232 case NetworkAdapterType_I82543GC:
233 case NetworkAdapterType_I82545EM:
234#endif
235#ifdef VBOX_WITH_VIRTIO
236 case NetworkAdapterType_Virtio:
237#endif /* VBOX_WITH_VIRTIO */
238 break;
239 default:
240 return setError(E_FAIL,
241 tr("Invalid network adapter type '%d'"),
242 aAdapterType);
243 }
244
245 if (mData->mAdapterType != aAdapterType)
246 {
247 mData.backup();
248 mData->mAdapterType = aAdapterType;
249
250 m_fModified = true;
251 // leave the lock before informing callbacks
252 alock.release();
253
254 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
255 mParent->setModified(Machine::IsModified_NetworkAdapters);
256 mlock.release();
257
258 /* Changing the network adapter type during runtime is not allowed,
259 * therefore no immediate change in CFGM logic => changeAdapter=FALSE. */
260 mParent->onNetworkAdapterChange(this, FALSE);
261 }
262
263 return S_OK;
264}
265
266STDMETHODIMP NetworkAdapter::COMGETTER(Slot) (ULONG *aSlot)
267{
268 CheckComArgOutPointerValid(aSlot);
269
270 AutoCaller autoCaller(this);
271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
272
273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
274
275 *aSlot = mData->mSlot;
276
277 return S_OK;
278}
279
280STDMETHODIMP NetworkAdapter::COMGETTER(Enabled) (BOOL *aEnabled)
281{
282 CheckComArgOutPointerValid(aEnabled);
283
284 AutoCaller autoCaller(this);
285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
286
287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
288
289 *aEnabled = mData->mEnabled;
290
291 return S_OK;
292}
293
294STDMETHODIMP NetworkAdapter::COMSETTER(Enabled) (BOOL aEnabled)
295{
296 AutoCaller autoCaller(this);
297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
298
299 /* the machine needs to be mutable */
300 AutoMutableStateDependency adep(mParent);
301 if (FAILED(adep.rc())) return adep.rc();
302
303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
304
305 if (mData->mEnabled != aEnabled)
306 {
307 mData.backup();
308 mData->mEnabled = aEnabled;
309
310 m_fModified = true;
311 // leave the lock before informing callbacks
312 alock.release();
313
314 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
315 mParent->setModified(Machine::IsModified_NetworkAdapters);
316 mlock.release();
317
318 /* Disabling the network adapter during runtime is not allowed
319 * therefore no immediate change in CFGM logic => changeAdapter=FALSE. */
320 mParent->onNetworkAdapterChange(this, FALSE);
321 }
322
323 return S_OK;
324}
325
326STDMETHODIMP NetworkAdapter::COMGETTER(MACAddress)(BSTR *aMACAddress)
327{
328 CheckComArgOutPointerValid(aMACAddress);
329
330 AutoCaller autoCaller(this);
331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
332
333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
334
335 ComAssertRet(!!mData->mMACAddress, E_FAIL);
336
337 mData->mMACAddress.cloneTo(aMACAddress);
338
339 return S_OK;
340}
341
342STDMETHODIMP NetworkAdapter::COMSETTER(MACAddress)(IN_BSTR aMACAddress)
343{
344 AutoCaller autoCaller(this);
345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
346
347 /* the machine needs to be mutable */
348 AutoMutableStateDependency adep(mParent);
349 if (FAILED(adep.rc())) return adep.rc();
350
351 HRESULT rc = S_OK;
352 bool emitChangeEvent = false;
353
354 /*
355 * Are we supposed to generate a MAC?
356 */
357 if (!aMACAddress || !*aMACAddress)
358 {
359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
360 mData.backup();
361
362 generateMACAddress();
363 emitChangeEvent = true;
364
365 m_fModified = true;
366 // leave the lock before informing callbacks
367 alock.release();
368
369 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
370 mParent->setModified(Machine::IsModified_NetworkAdapters);
371 mlock.release();
372 }
373 else
374 {
375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
376 if (mData->mMACAddress != aMACAddress)
377 {
378 /*
379 * Verify given MAC address
380 */
381 Utf8Str macAddressUtf = aMACAddress;
382 char *macAddressStr = macAddressUtf.mutableRaw();
383 int i = 0;
384 while ((i < 13) && macAddressStr && *macAddressStr && (rc == S_OK))
385 {
386 char c = *macAddressStr;
387 /* canonicalize hex digits to capital letters */
388 if (c >= 'a' && c <= 'f')
389 {
390 /** @todo the runtime lacks an ascii lower/upper conv */
391 c &= 0xdf;
392 *macAddressStr = c;
393 }
394 /* we only accept capital letters */
395 if (((c < '0') || (c > '9')) &&
396 ((c < 'A') || (c > 'F')))
397 rc = setError(E_INVALIDARG, tr("Invalid MAC address format"));
398 /* the second digit must have even value for unicast addresses */
399 if ((i == 1) && (!!(c & 1) == (c >= '0' && c <= '9')))
400 rc = setError(E_INVALIDARG, tr("Invalid MAC address format"));
401
402 macAddressStr++;
403 i++;
404 }
405 /* we must have parsed exactly 12 characters */
406 if (i != 12)
407 rc = setError(E_INVALIDARG, tr("Invalid MAC address format"));
408
409 if (SUCCEEDED(rc))
410 {
411 mData.backup();
412
413 mData->mMACAddress = macAddressUtf;
414
415 emitChangeEvent = true;
416
417 m_fModified = true;
418 // leave the lock before informing callbacks
419 alock.release();
420
421 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
422 mParent->setModified(Machine::IsModified_NetworkAdapters);
423 mlock.release();
424 }
425 }
426 }
427
428 // we have left the lock in any case at this point
429
430 if (emitChangeEvent)
431 {
432 /* Changing the MAC via the Main API during runtime is not allowed,
433 * therefore no immediate change in CFGM logic => changeAdapter=FALSE. */
434 mParent->onNetworkAdapterChange(this, FALSE);
435 }
436
437 return rc;
438}
439
440STDMETHODIMP NetworkAdapter::COMGETTER(AttachmentType)(
441 NetworkAttachmentType_T *aAttachmentType)
442{
443 CheckComArgOutPointerValid(aAttachmentType);
444
445 AutoCaller autoCaller(this);
446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
447
448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
449
450 *aAttachmentType = mData->mAttachmentType;
451
452 return S_OK;
453}
454
455STDMETHODIMP NetworkAdapter::COMGETTER(HostInterface)(BSTR *aHostInterface)
456{
457 CheckComArgOutPointerValid(aHostInterface);
458
459 AutoCaller autoCaller(this);
460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
461
462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
463
464 mData->mHostInterface.cloneTo(aHostInterface);
465
466 return S_OK;
467}
468
469STDMETHODIMP NetworkAdapter::COMSETTER(HostInterface)(IN_BSTR aHostInterface)
470{
471 Bstr bstrEmpty("");
472 if (!aHostInterface)
473 aHostInterface = bstrEmpty;
474
475 AutoCaller autoCaller(this);
476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
477
478 /* the machine needs to be mutable */
479 AutoMutableStateDependency adep(mParent);
480 if (FAILED(adep.rc())) return adep.rc();
481
482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
483
484 if (mData->mHostInterface != aHostInterface)
485 {
486 mData.backup();
487 mData->mHostInterface = aHostInterface;
488
489 m_fModified = true;
490 // leave the lock before informing callbacks
491 alock.release();
492
493 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
494 mParent->setModified(Machine::IsModified_NetworkAdapters);
495 mlock.release();
496
497 /* When changing the host adapter, adapt the CFGM logic to make this
498 * change immediately effect and to notifiy the guest that the network
499 * might have changed, therefore changeAdapter=TRUE. */
500 mParent->onNetworkAdapterChange(this, TRUE);
501 }
502
503 return S_OK;
504}
505
506STDMETHODIMP NetworkAdapter::COMGETTER(InternalNetwork) (BSTR *aInternalNetwork)
507{
508 CheckComArgOutPointerValid(aInternalNetwork);
509
510 AutoCaller autoCaller(this);
511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
512
513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
514
515 mData->mInternalNetwork.cloneTo(aInternalNetwork);
516
517 return S_OK;
518}
519
520STDMETHODIMP NetworkAdapter::COMSETTER(InternalNetwork) (IN_BSTR aInternalNetwork)
521{
522 AutoCaller autoCaller(this);
523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
524
525 /* the machine needs to be mutable */
526 AutoMutableStateDependency adep(mParent);
527 if (FAILED(adep.rc())) return adep.rc();
528
529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
530
531 if (mData->mInternalNetwork != aInternalNetwork)
532 {
533 /* if an empty/null string is to be set, internal networking must be
534 * turned off */
535 if ( (aInternalNetwork == NULL || *aInternalNetwork == '\0')
536 && mData->mAttachmentType == NetworkAttachmentType_Internal)
537 {
538 return setError(E_FAIL,
539 tr("Empty or null internal network name is not valid"));
540 }
541
542 mData.backup();
543 mData->mInternalNetwork = aInternalNetwork;
544
545 m_fModified = true;
546 // leave the lock before informing callbacks
547 alock.release();
548
549 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
550 mParent->setModified(Machine::IsModified_NetworkAdapters);
551 mlock.release();
552
553 /* When changing the internal network, adapt the CFGM logic to make this
554 * change immediately effect and to notifiy the guest that the network
555 * might have changed, therefore changeAdapter=TRUE. */
556 mParent->onNetworkAdapterChange(this, TRUE);
557 }
558
559 return S_OK;
560}
561
562STDMETHODIMP NetworkAdapter::COMGETTER(NATNetwork) (BSTR *aNATNetwork)
563{
564 CheckComArgOutPointerValid(aNATNetwork);
565
566 AutoCaller autoCaller(this);
567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
568
569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
570
571 mData->mNATNetwork.cloneTo(aNATNetwork);
572
573 return S_OK;
574}
575
576STDMETHODIMP NetworkAdapter::COMSETTER(NATNetwork) (IN_BSTR aNATNetwork)
577{
578 Bstr bstrEmpty("");
579 if (!aNATNetwork)
580 aNATNetwork = bstrEmpty;
581
582 AutoCaller autoCaller(this);
583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
584
585 /* the machine needs to be mutable */
586 AutoMutableStateDependency adep(mParent);
587 if (FAILED(adep.rc())) return adep.rc();
588
589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
590
591 if (mData->mNATNetwork != aNATNetwork)
592 {
593 mData.backup();
594 mData->mNATNetwork = aNATNetwork;
595
596 m_fModified = true;
597 // leave the lock before informing callbacks
598 alock.release();
599
600 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
601 mParent->setModified(Machine::IsModified_NetworkAdapters);
602 mlock.release();
603
604 /* Changing the NAT network isn't allowed during runtime, therefore
605 * no immediate replug in CFGM logic => changeAdapter=FALSE */
606 mParent->onNetworkAdapterChange(this, FALSE);
607 }
608
609 return S_OK;
610}
611
612STDMETHODIMP NetworkAdapter::COMGETTER(CableConnected) (BOOL *aConnected)
613{
614 CheckComArgOutPointerValid(aConnected);
615
616 AutoCaller autoCaller(this);
617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
618
619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
620
621 *aConnected = mData->mCableConnected;
622
623 return S_OK;
624}
625
626STDMETHODIMP NetworkAdapter::COMSETTER(CableConnected) (BOOL aConnected)
627{
628 AutoCaller autoCaller(this);
629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
630
631 /* the machine needs to be mutable */
632 AutoMutableStateDependency adep(mParent);
633 if (FAILED(adep.rc())) return adep.rc();
634
635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
636
637 if (aConnected != mData->mCableConnected)
638 {
639 mData.backup();
640 mData->mCableConnected = aConnected;
641
642 m_fModified = true;
643 // leave the lock before informing callbacks
644 alock.release();
645
646 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
647 mParent->setModified(Machine::IsModified_NetworkAdapters);
648 mlock.release();
649
650 /* No change in CFGM logic => changeAdapter=FALSE. */
651 mParent->onNetworkAdapterChange(this, FALSE);
652 }
653
654 return S_OK;
655}
656
657STDMETHODIMP NetworkAdapter::COMGETTER(LineSpeed) (ULONG *aSpeed)
658{
659 CheckComArgOutPointerValid(aSpeed);
660
661 AutoCaller autoCaller(this);
662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
663
664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
665
666 *aSpeed = mData->mLineSpeed;
667
668 return S_OK;
669}
670
671STDMETHODIMP NetworkAdapter::COMSETTER(LineSpeed) (ULONG aSpeed)
672{
673 AutoCaller autoCaller(this);
674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
675
676 /* the machine needs to be mutable */
677 AutoMutableStateDependency adep(mParent);
678 if (FAILED(adep.rc())) return adep.rc();
679
680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
681
682 if (aSpeed != mData->mLineSpeed)
683 {
684 mData.backup();
685 mData->mLineSpeed = aSpeed;
686
687 m_fModified = true;
688 // leave the lock before informing callbacks
689 alock.release();
690
691 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
692 mParent->setModified(Machine::IsModified_NetworkAdapters);
693 mlock.release();
694
695 /* No change in CFGM logic => changeAdapter=FALSE. */
696 mParent->onNetworkAdapterChange(this, FALSE);
697 }
698
699 return S_OK;
700}
701
702STDMETHODIMP NetworkAdapter::COMGETTER(TraceEnabled) (BOOL *aEnabled)
703{
704 CheckComArgOutPointerValid(aEnabled);
705
706 AutoCaller autoCaller(this);
707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
708
709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
710
711 *aEnabled = mData->mTraceEnabled;
712 return S_OK;
713}
714
715STDMETHODIMP NetworkAdapter::COMSETTER(TraceEnabled) (BOOL aEnabled)
716{
717 AutoCaller autoCaller(this);
718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
719
720 /* the machine needs to be mutable */
721 AutoMutableStateDependency adep(mParent);
722 if (FAILED(adep.rc())) return adep.rc();
723
724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
725
726 if (aEnabled != mData->mTraceEnabled)
727 {
728 mData.backup();
729 mData->mTraceEnabled = aEnabled;
730
731 m_fModified = true;
732 // leave the lock before informing callbacks
733 alock.release();
734
735 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
736 mParent->setModified(Machine::IsModified_NetworkAdapters);
737 mlock.release();
738
739 /* Adapt the CFGM logic changeAdapter=TRUE */
740 mParent->onNetworkAdapterChange(this, TRUE);
741 }
742
743 return S_OK;
744}
745
746STDMETHODIMP NetworkAdapter::COMGETTER(TraceFile) (BSTR *aTraceFile)
747{
748 CheckComArgOutPointerValid(aTraceFile);
749
750 AutoCaller autoCaller(this);
751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
752
753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
754
755 mData->mTraceFile.cloneTo(aTraceFile);
756
757 return S_OK;
758}
759
760STDMETHODIMP NetworkAdapter::COMSETTER(TraceFile) (IN_BSTR aTraceFile)
761{
762 AutoCaller autoCaller(this);
763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
764
765 /* the machine needs to be mutable */
766 AutoMutableStateDependency adep(mParent);
767 if (FAILED(adep.rc())) return adep.rc();
768
769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
770
771 if (mData->mTraceFile != aTraceFile)
772 {
773 mData.backup();
774 mData->mTraceFile = aTraceFile;
775
776 m_fModified = true;
777 // leave the lock before informing callbacks
778 alock.release();
779
780 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
781 mParent->setModified(Machine::IsModified_NetworkAdapters);
782 mlock.release();
783
784 /* No change in CFGM logic => changeAdapter=FALSE. */
785 mParent->onNetworkAdapterChange(this, FALSE);
786 }
787
788 return S_OK;
789}
790
791STDMETHODIMP NetworkAdapter::COMGETTER(NatDriver) (INATEngine **aNatDriver)
792{
793 CheckComArgOutPointerValid(aNatDriver);
794
795 AutoCaller autoCaller(this);
796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
797
798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
799
800 mNATEngine.queryInterfaceTo(aNatDriver);
801
802 return S_OK;
803}
804
805STDMETHODIMP NetworkAdapter::COMGETTER(BootPriority) (ULONG *aBootPriority)
806{
807 CheckComArgOutPointerValid(aBootPriority);
808
809 AutoCaller autoCaller(this);
810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
811
812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
813
814 *aBootPriority = mData->mBootPriority;
815
816 return S_OK;
817}
818
819STDMETHODIMP NetworkAdapter::COMSETTER(BootPriority) (ULONG aBootPriority)
820{
821 AutoCaller autoCaller(this);
822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
823
824 /* the machine needs to be mutable */
825 AutoMutableStateDependency adep(mParent);
826 if (FAILED(adep.rc())) return adep.rc();
827
828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
829
830 if (aBootPriority != mData->mBootPriority)
831 {
832 mData.backup();
833 mData->mBootPriority = aBootPriority;
834
835 m_fModified = true;
836 // leave the lock before informing callbacks
837 alock.release();
838
839 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
840 mParent->setModified(Machine::IsModified_NetworkAdapters);
841 mlock.release();
842
843 /* No change in CFGM logic => changeAdapter=FALSE. */
844 mParent->onNetworkAdapterChange(this, FALSE);
845 }
846
847 return S_OK;
848}
849
850// INetworkAdapter methods
851////////////////////////////////////////////////////////////////////////////////
852
853STDMETHODIMP NetworkAdapter::AttachToNAT()
854{
855 AutoCaller autoCaller(this);
856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
857
858 /* the machine needs to be mutable */
859 AutoMutableStateDependency adep(mParent);
860 if (FAILED(adep.rc())) return adep.rc();
861
862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
863
864 if (mData->mAttachmentType != NetworkAttachmentType_NAT)
865 {
866 mData.backup();
867
868 // Commented this for now as it resets the parameter mData->mNATNetwork
869 // which is essential while changing the Attachment dynamically.
870 //detach();
871
872 mData->mAttachmentType = NetworkAttachmentType_NAT;
873
874 m_fModified = true;
875 // leave the lock before informing callbacks
876 alock.release();
877
878 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
879 mParent->setModified(Machine::IsModified_NetworkAdapters);
880 mlock.release();
881
882 /* Adapt the CFGM logic and notify the guest => changeAdapter=TRUE. */
883 HRESULT rc = mParent->onNetworkAdapterChange(this, TRUE);
884 if (FAILED(rc))
885 {
886 /* If changing the attachment failed then we can't assume
887 * that the previous attachment will attach correctly
888 * and thus return error along with dettaching all
889 * attachments.
890 */
891 Detach();
892 return rc;
893 }
894 }
895
896 return S_OK;
897}
898
899STDMETHODIMP NetworkAdapter::AttachToBridgedInterface()
900{
901 AutoCaller autoCaller(this);
902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
903
904 /* the machine needs to be mutable */
905 AutoMutableStateDependency adep(mParent);
906 if (FAILED(adep.rc())) return adep.rc();
907
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 /* don't do anything if we're already host interface attached */
911 if (mData->mAttachmentType != NetworkAttachmentType_Bridged)
912 {
913 mData.backup();
914
915 /* first detach the current attachment */
916 // Commented this for now as it reset the parameter mData->mHostInterface
917 // which is essential while changing the Attachment dynamically.
918 //detach();
919
920 mData->mAttachmentType = NetworkAttachmentType_Bridged;
921
922 m_fModified = true;
923 // leave the lock before informing callbacks
924 alock.release();
925
926 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
927 mParent->setModified(Machine::IsModified_NetworkAdapters);
928 mlock.release();
929
930 /* Adapt the CFGM logic and notify the guest => changeAdapter=TRUE. */
931 HRESULT rc = mParent->onNetworkAdapterChange(this, TRUE);
932 if (FAILED(rc))
933 {
934 /* If changing the attachment failed then we can't assume that the
935 * previous attachment will attach correctly and thus return error
936 * along with dettaching all attachments.
937 */
938 Detach();
939 return rc;
940 }
941 }
942
943 return S_OK;
944}
945
946STDMETHODIMP NetworkAdapter::AttachToInternalNetwork()
947{
948 AutoCaller autoCaller(this);
949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
950
951 /* the machine needs to be mutable */
952 AutoMutableStateDependency adep(mParent);
953 if (FAILED(adep.rc())) return adep.rc();
954
955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 /* don't do anything if we're already internal network attached */
958 if (mData->mAttachmentType != NetworkAttachmentType_Internal)
959 {
960 mData.backup();
961
962 /* first detach the current attachment */
963 // Commented this for now as it reset the parameter mData->mInternalNetwork
964 // which is essential while changing the Attachment dynamically.
965 //detach();
966
967 /* there must an internal network name */
968 if (mData->mInternalNetwork.isEmpty())
969 {
970 LogRel (("Internal network name not defined, "
971 "setting to default \"intnet\"\n"));
972 mData->mInternalNetwork = "intnet";
973 }
974
975 mData->mAttachmentType = NetworkAttachmentType_Internal;
976
977 m_fModified = true;
978 // leave the lock before informing callbacks
979 alock.release();
980
981 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
982 mParent->setModified(Machine::IsModified_NetworkAdapters);
983 mlock.release();
984
985 /* Adapt the CFGM logic and notify the guest => changeAdapter=TRUE. */
986 HRESULT rc = mParent->onNetworkAdapterChange(this, TRUE);
987 if (FAILED(rc))
988 {
989 /* If changing the attachment failed then we can't assume
990 * that the previous attachment will attach correctly
991 * and thus return error along with dettaching all
992 * attachments.
993 */
994 Detach();
995 return rc;
996 }
997 }
998
999 return S_OK;
1000}
1001
1002STDMETHODIMP NetworkAdapter::AttachToHostOnlyInterface()
1003{
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 /* the machine needs to be mutable */
1008 AutoMutableStateDependency adep(mParent);
1009 if (FAILED(adep.rc())) return adep.rc();
1010
1011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 /* don't do anything if we're already host interface attached */
1014 if (mData->mAttachmentType != NetworkAttachmentType_HostOnly)
1015 {
1016 mData.backup();
1017
1018 /* first detach the current attachment */
1019 // Commented this for now as it reset the parameter mData->mHostInterface
1020 // which is essential while changing the Attachment dynamically.
1021 //detach();
1022
1023 mData->mAttachmentType = NetworkAttachmentType_HostOnly;
1024
1025 m_fModified = true;
1026 // leave the lock before informing callbacks
1027 alock.release();
1028
1029 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
1030 mParent->setModified(Machine::IsModified_NetworkAdapters);
1031 mlock.release();
1032
1033 /* Adapt the CFGM logic and notify the guest => changeAdpater=TRUE. */
1034 HRESULT rc = mParent->onNetworkAdapterChange(this, TRUE);
1035 if (FAILED(rc))
1036 {
1037 /* If changing the attachment failed then we can't assume
1038 * that the previous attachment will attach correctly
1039 * and thus return error along with dettaching all
1040 * attachments.
1041 */
1042 Detach();
1043 return rc;
1044 }
1045 }
1046
1047 return S_OK;
1048}
1049
1050STDMETHODIMP NetworkAdapter::Detach()
1051{
1052 AutoCaller autoCaller(this);
1053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1054
1055 /* the machine needs to be mutable */
1056 AutoMutableStateDependency adep(mParent);
1057 if (FAILED(adep.rc())) return adep.rc();
1058
1059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 if (mData->mAttachmentType != NetworkAttachmentType_Null)
1062 {
1063 mData.backup();
1064
1065 detach();
1066
1067 m_fModified = true;
1068 // leave the lock before informing callbacks
1069 alock.release();
1070
1071 AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, no need to lock
1072 mParent->setModified(Machine::IsModified_NetworkAdapters);
1073 mlock.release();
1074
1075 /* adapt the CFGM logic and notify the guest => changeAdapter=TRUE. */
1076 mParent->onNetworkAdapterChange(this, TRUE);
1077 }
1078
1079 return S_OK;
1080}
1081
1082// public methods only for internal purposes
1083////////////////////////////////////////////////////////////////////////////////
1084
1085/**
1086 * Loads settings from the given adapter node.
1087 * May be called once right after this object creation.
1088 *
1089 * @param aAdapterNode <Adapter> node.
1090 *
1091 * @note Locks this object for writing.
1092 */
1093HRESULT NetworkAdapter::loadSettings(const settings::NetworkAdapter &data)
1094{
1095 AutoCaller autoCaller(this);
1096 AssertComRCReturnRC(autoCaller.rc());
1097
1098 /* Note: we assume that the default values for attributes of optional
1099 * nodes are assigned in the Data::Data() constructor and don't do it
1100 * here. It implies that this method may only be called after constructing
1101 * a new BIOSSettings object while all its data fields are in the default
1102 * values. Exceptions are fields whose creation time defaults don't match
1103 * values that should be applied when these fields are not explicitly set
1104 * in the settings file (for backwards compatibility reasons). This takes
1105 * place when a setting of a newly created object must default to A while
1106 * the same setting of an object loaded from the old settings file must
1107 * default to B. */
1108
1109 HRESULT rc = S_OK;
1110
1111 mData->mAdapterType = data.type;
1112 mData->mEnabled = data.fEnabled;
1113 /* MAC address (can be null) */
1114 rc = COMSETTER(MACAddress)(Bstr(data.strMACAddress));
1115 if (FAILED(rc)) return rc;
1116 /* cable (required) */
1117 mData->mCableConnected = data.fCableConnected;
1118 /* line speed (defaults to 100 Mbps) */
1119 mData->mLineSpeed = data.ulLineSpeed;
1120 /* tracing (defaults to false) */
1121 mData->mTraceEnabled = data.fTraceEnabled;
1122 mData->mTraceFile = data.strTraceFile;
1123 /* boot priority (defaults to 0, i.e. lowest) */
1124 mData->mBootPriority = data.ulBootPriority;
1125
1126 switch (data.mode)
1127 {
1128 case NetworkAttachmentType_NAT:
1129 mNATEngine->loadSettings(data.nat);
1130 rc = AttachToNAT();
1131 if (FAILED(rc)) return rc;
1132 break;
1133
1134 case NetworkAttachmentType_Bridged:
1135 rc = COMSETTER(HostInterface)(Bstr(data.strName));
1136 if (FAILED(rc)) return rc;
1137 rc = AttachToBridgedInterface();
1138 if (FAILED(rc)) return rc;
1139 break;
1140
1141 case NetworkAttachmentType_Internal:
1142 mData->mInternalNetwork = data.strName;
1143 Assert(!mData->mInternalNetwork.isEmpty());
1144
1145 rc = AttachToInternalNetwork();
1146 if (FAILED(rc)) return rc;
1147 break;
1148
1149 case NetworkAttachmentType_HostOnly:
1150#if defined(VBOX_WITH_NETFLT)
1151 rc = COMSETTER(HostInterface)(Bstr(data.strName));
1152 if (FAILED(rc)) return rc;
1153#endif
1154 rc = AttachToHostOnlyInterface();
1155 if (FAILED(rc)) return rc;
1156 break;
1157
1158 case NetworkAttachmentType_Null:
1159 rc = Detach();
1160 if (FAILED(rc)) return rc;
1161 break;
1162 }
1163 if (data.fHasDisabledNAT)
1164 mNATEngine->loadSettings(data.nat);
1165
1166 // after loading settings, we are no longer different from the XML on disk
1167 m_fModified = false;
1168
1169 return S_OK;
1170}
1171
1172/**
1173 * Saves settings to the given adapter node.
1174 *
1175 * Note that the given Adapter node is comletely empty on input.
1176 *
1177 * @param aAdapterNode <Adapter> node.
1178 *
1179 * @note Locks this object for reading.
1180 */
1181HRESULT NetworkAdapter::saveSettings(settings::NetworkAdapter &data)
1182{
1183 AutoCaller autoCaller(this);
1184 AssertComRCReturnRC(autoCaller.rc());
1185
1186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 data.fEnabled = !!mData->mEnabled;
1189 data.strMACAddress = mData->mMACAddress;
1190 data.fCableConnected = !!mData->mCableConnected;
1191
1192 data.ulLineSpeed = mData->mLineSpeed;
1193
1194 data.fTraceEnabled = !!mData->mTraceEnabled;
1195
1196 data.strTraceFile = mData->mTraceFile;
1197
1198 data.ulBootPriority = mData->mBootPriority;
1199
1200 data.type = mData->mAdapterType;
1201
1202 switch (data.mode = mData->mAttachmentType)
1203 {
1204 case NetworkAttachmentType_Null:
1205 data.strName.setNull();
1206 break;
1207
1208 case NetworkAttachmentType_NAT:
1209 data.fHasDisabledNAT = 0;
1210 mNATEngine->commit();
1211 mNATEngine->saveSettings(data.nat);
1212 break;
1213
1214 case NetworkAttachmentType_Bridged:
1215 data.strName = mData->mHostInterface;
1216 break;
1217
1218 case NetworkAttachmentType_Internal:
1219 data.strName = mData->mInternalNetwork;
1220 break;
1221
1222 case NetworkAttachmentType_HostOnly:
1223 data.strName = mData->mHostInterface;
1224 break;
1225 }
1226
1227 if (data.mode != NetworkAttachmentType_NAT)
1228 {
1229 data.fHasDisabledNAT = 1; /* ??? */
1230 mNATEngine->commit();
1231 mNATEngine->saveSettings(data.nat);
1232 }
1233
1234 // after saving settings, we are no longer different from the XML on disk
1235 m_fModified = false;
1236
1237 return S_OK;
1238}
1239
1240/**
1241 * Returns true if any setter method has modified settings of this instance.
1242 * @return
1243 */
1244bool NetworkAdapter::isModified() {
1245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1246 bool fChanged = m_fModified;
1247 fChanged |= (mData->mAdapterType == NetworkAttachmentType_NAT? mNATEngine->isModified() : false);
1248 return fChanged;
1249}
1250
1251/**
1252 * @note Locks this object for writing.
1253 */
1254void NetworkAdapter::rollback()
1255{
1256 /* sanity */
1257 AutoCaller autoCaller(this);
1258 AssertComRCReturnVoid(autoCaller.rc());
1259
1260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 mData.rollback();
1263}
1264
1265/**
1266 * @note Locks this object for writing, together with the peer object (also
1267 * for writing) if there is one.
1268 */
1269void NetworkAdapter::commit()
1270{
1271 /* sanity */
1272 AutoCaller autoCaller(this);
1273 AssertComRCReturnVoid (autoCaller.rc());
1274
1275 /* sanity too */
1276 AutoCaller peerCaller (mPeer);
1277 AssertComRCReturnVoid (peerCaller.rc());
1278
1279 /* lock both for writing since we modify both (mPeer is "master" so locked
1280 * first) */
1281 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
1282
1283 if (mData.isBackedUp())
1284 {
1285 mData.commit();
1286 if (mPeer)
1287 {
1288 /* attach new data to the peer and reshare it */
1289 mPeer->mData.attach (mData);
1290 }
1291 }
1292}
1293
1294/**
1295 * @note Locks this object for writing, together with the peer object
1296 * represented by @a aThat (locked for reading).
1297 */
1298void NetworkAdapter::copyFrom (NetworkAdapter *aThat)
1299{
1300 AssertReturnVoid (aThat != NULL);
1301
1302 /* sanity */
1303 AutoCaller autoCaller(this);
1304 AssertComRCReturnVoid (autoCaller.rc());
1305
1306 /* sanity too */
1307 AutoCaller thatCaller (aThat);
1308 AssertComRCReturnVoid (thatCaller.rc());
1309
1310 /* peer is not modified, lock it for reading (aThat is "master" so locked
1311 * first) */
1312 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
1313 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
1314
1315 /* this will back up current data */
1316 mData.assignCopy (aThat->mData);
1317}
1318
1319void NetworkAdapter::applyDefaults (GuestOSType *aOsType)
1320{
1321 AssertReturnVoid (aOsType != NULL);
1322
1323 /* sanity */
1324 AutoCaller autoCaller(this);
1325 AssertComRCReturnVoid (autoCaller.rc());
1326
1327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 bool e1000enabled = false;
1330#ifdef VBOX_WITH_E1000
1331 e1000enabled = true;
1332#endif // VBOX_WITH_E1000
1333
1334 NetworkAdapterType_T defaultType = aOsType->networkAdapterType();
1335
1336 /* Set default network adapter for this OS type */
1337 if (defaultType == NetworkAdapterType_I82540EM ||
1338 defaultType == NetworkAdapterType_I82543GC ||
1339 defaultType == NetworkAdapterType_I82545EM)
1340 {
1341 if (e1000enabled) mData->mAdapterType = defaultType;
1342 }
1343 else mData->mAdapterType = defaultType;
1344
1345 /* Enable and connect the first one adapter to the NAT */
1346 if (mData->mSlot == 0)
1347 {
1348 mData->mEnabled = true;
1349 mData->mAttachmentType = NetworkAttachmentType_NAT;
1350 mData->mCableConnected = true;
1351 }
1352}
1353
1354// private methods
1355////////////////////////////////////////////////////////////////////////////////
1356
1357/**
1358 * Worker routine for detach handling. No locking, no notifications.
1359
1360 * @note Must be called from under the object's write lock.
1361 */
1362void NetworkAdapter::detach()
1363{
1364 AssertReturnVoid (isWriteLockOnCurrentThread());
1365
1366 switch (mData->mAttachmentType)
1367 {
1368 case NetworkAttachmentType_Null:
1369 {
1370 /* nothing to do here */
1371 break;
1372 }
1373 case NetworkAttachmentType_NAT:
1374 {
1375 break;
1376 }
1377 case NetworkAttachmentType_Bridged:
1378 {
1379 /* reset handle and device name */
1380 mData->mHostInterface = "";
1381 break;
1382 }
1383 case NetworkAttachmentType_Internal:
1384 {
1385 mData->mInternalNetwork.setNull();
1386 break;
1387 }
1388 case NetworkAttachmentType_HostOnly:
1389 {
1390#if defined(VBOX_WITH_NETFLT)
1391 /* reset handle and device name */
1392 mData->mHostInterface = "";
1393#endif
1394 break;
1395 }
1396 }
1397
1398 mData->mAttachmentType = NetworkAttachmentType_Null;
1399}
1400
1401/**
1402 * Generates a new unique MAC address based on our vendor ID and
1403 * parts of a GUID.
1404 *
1405 * @note Must be called from under the object's write lock or within the init
1406 * span.
1407 */
1408void NetworkAdapter::generateMACAddress()
1409{
1410 /*
1411 * Our strategy is as follows: the first three bytes are our fixed
1412 * vendor ID (080027). The remaining 3 bytes will be taken from the
1413 * start of a GUID. This is a fairly safe algorithm.
1414 */
1415 char strMAC[13];
1416 Guid guid;
1417 guid.create();
1418 RTStrPrintf (strMAC, sizeof(strMAC), "080027%02X%02X%02X",
1419 guid.ptr()->au8[0], guid.ptr()->au8[1], guid.ptr()->au8[2]);
1420 LogFlowThisFunc(("generated MAC: '%s'\n", strMAC));
1421 mData->mMACAddress = strMAC;
1422}
1423/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette