-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathPyRIC.tex
1174 lines (1057 loc) · 53.5 KB
/
PyRIC.tex
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
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 2016 Dale V. Patterson ([email protected])
%
%This program is free software: you can redistribute it and/or modify it under
%the terms of the GNU General Public License as published by the Free Software
%Foundation, either version 3 of the License, or (at your option) any later
%version.
%
%Redistribution and use in source and binary forms, with or without modifications,
%are permitted provided that the following conditions are met:
% o Redistributions of source code must retain the above copyright notice, this
% list of conditions and the following disclaimer.
% o Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
% o Neither the name of the orginal author Dale V. Patterson nor the names of any
% contributors may be used to endorse or promote products derived from this
% software without specific prior written permission.
%
% __name__ = 'User Guide'
%__license__ = 'GPLv3'
%__version__ = '0.0.7'
%__date__ = 'December 2016'
%__author__ = 'Dale Patterson'
%__maintainer__ = 'Dale Patterson'
%__email__ = '[email protected]'
%__status__ = 'Production'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass[11pt]{article}
\usepackage[utf8]{inputenc}
\setlength{\parindent}{0pt}
\usepackage{graphicx}
\usepackage{listings}
\usepackage{caption}
\usepackage{geometry}
\usepackage{color}
\usepackage{graphicx}
\usepackage[titletoc,toc,title]{appendix}
\usepackage[T1]{fontenc}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\geometry{left=1.0in,right=1.0in,top=1.0in,bottom=1.0in }
\lstset{
frame=top,
frame=bottom,
basicstyle=\small\normalfont\ ,
stepnumber=1,
numbersep=10pt,
tabsize=2,
extendedchars=true,
breaklines=true,
captionpos=b,
mathescape=true,
showspaces=false,
showtabs=false,
xleftmargin=17pt,
framexleftmargin=17pt,
framexrightmargin=17pt,
framexbottommargin=5pt,
framextopmargin=5pt,
showstringspaces=false,
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{magenta},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\footnotesize
}
\title{\includegraphics[scale=1]{logo}\\ PyRIC v0.1.6.4: User Manual}
\author{Dale V. Patterson\\ [email protected]}
\begin{document}
\maketitle
\tableofcontents
\section{About PyRIC}\label{sec:About}
PyRIC (is a Linux only) library providing wireless developers and pentesters the
ability to identify, enumerate and manipulate their system's wireless cards
programmatically in Python. Pentesting applications and scripts written in Python
have increased dramatically in recent years. However, these tools still rely on
Linux command lines tools to setup and prepare and restore the system for use.
Until now. Why use subprocess.Popen, regular expressions and str.find to interact
with your wireless cards? PyRIC puts iw, ifconfig, rfkill, udevadm, airmon-ng and
macchanger in your hands (or your program). \\
PyRIC is designed with Python 2.7 in mind but has now been made compatible with
Python 3.5. It will also work on Python 3.0 but you will have to hard code the
command line options in the two examples as Python 3.0 does not include the module
argparse
PyRIC is:
\begin{enumerate}
\item \textbf{Pythonic}: No ctypes, SWIG etc. PyRIC redefines C header files as
Python and uses sockets to communicate with kernel.
\item \textbf{Self-sufficient}: No third-party files used, PyRIC is completely self-
contained
\item \textbf{Fast}: (relatively speaking) PyRIC is faster than using iw through
subprocess.Popen
\item \textbf{Parseless}: Get the output you without parsing output from iw. Never
worry about iw updates and rewriting your parsers.
\item \textbf{Easy}: If you can use iw, you can use PyRIC
\end{enumerate}
At it's heart, PyRIC is a Python port of (a subset of) iw and by extension, a
Python port of Netlink w.r.t nl80211 functionality. The original goal of PyRIC
was to provide a simple interface to the underlying nl80211 kernel support,
handling the complex operations of Netlink seamlessy while maintaining a minimum
of "code walking" to understand, modify and extend. But, why stop there? Since
it's initial inception, PyRIC has grown to include ioctl support to replicate
features of ifconfig such as getting or setting the mac address and has recently
implemented rkill support to soft block or unblock wireless cards.\\
While users can utilize libnl.py to communicate directly with the kernel, the
true utility of PyRIC is pyw.py. Like iw, pyw provides an interface/buffer
between the caller and the kernel, handling all message construction, parsing
and data transfer transparently and without requiring any Netlink knowledge or
experience. \\
At this time, PyRIC can:
\begin{itemize}
\item enumerate interfaces and wireless interfaces,
\item identify a cards driver, chipset and manufacturer,
\item get/set hardware address,
\item get/set ip4 address, netmask and or broadcast,
\item turn card on/off,
\item get supported standards, commands or modes,
\item get if info,
\item get dev info,
\item get phy info,
\item get link info,
\item get STA (connected AP) info,
\item get/set regulatory domain,
\item get/set mode,
\item get/set coverage class, RTS threshold, Fragmentation threshold and retry
limits,
\item add/delete interfaces,
\item determine if a card is connected,
\item get link info for a connected card,
\item enumerate ISM and UNII channels,
\item block/unblock rfkill devices.
\end{itemize}
And, through libnl.py and libio.py, users can extend the above functionality by
creating additional commands.
\subsection{Background}
PyRIC arose out of a need in Wraith (https://github.com/wraith-wireless/wraith)
for Python nl80211/netlink and ioctl functionality. Originally, Wraith used
ifconfig, iwconfig and iw via subprocess.Popen and parsed the output. There
are obvious shortfalls with this method, especially in terms of iw that is
actively changing (revisions break the parser) and I started looking for an
open source alternative. There are several open source projects out there
such as pyroute, pymnl (and the python files included in the libnl source) but
they generally have either not been maintained recently or come with warnings.
I desired a simple interface to the underlying nl80211 kernel support that
handles the complex operations of netlink seamlessy while maintaining a minimum
of "code walking" to understand, modify and extend. I decided to write my own
because I do not need complete netlink functionality, only that provided by
generic netlink and within the nl80221 family. Additionally, for Wraith, I do
not need a full blown port of iw et. al. functionality to Python but only
require the ability to turn a wireless nic on/off, get/set the hwaddr, get/set
the channel, determine some properties of the card and add/delete interfaces.
So, why did I do this and why is it done "this" way? When I first started to
explore the idea of moving away from iw output parsing, I looked at the source
for iw, and existing Python ports. Just to figure out how to get the family id
for nl80211 required reading through five different source files with no
comments. To that extent, I have attempted to keep subclassing to a minimum,
the total number of classes to a minimum, combine files where possible and where
it makes since and keep the number of files required to be open simultaneously
in order to understand the methodology and follow the program to a minimum. One
can understand the PyRIC program flow with only two files open at any time namely,
pyw and libnl. In fact, only an understanding of pyw is required to add additional
commands although an understanding of libnl.py is helpful especially, if for
example, the code is to be extended to handle multicast or callbacks.
\subsection{Naming Conventions}
The terms interface, device and radio are all used interchangeably throughout to
refer to a network interface controller (NIC). The following terms will always
have one meaning:
\begin{itemize}
\item \textbf{dev} - the device name i.e. wlan0 or eth0 of a NIC,
\item \textbf{phy} - the physical index of a NIC i.e. the 0 in phy0,
\item \textbf{ifindex} - the interface index of a NIC,
\item \textbf{card} or \textbf{Card} - a NIC abstraction, an object used in pyw
functions see the following section for a description.
\end{itemize}
\subsection{Cards}
A Card is merely a wrapper around a tuple t = (phy index,device name,ifindex).
Since the underlying Netlink calls sometimes require the physical index, sometimes
the device name, and sometimes the ifindex, pyw functions\footnote{Not all functions
accept only a Card, devinfo() accepts either a Card or a dev, devadd accepts either a
Card or a ifindex and phyadd accepts only a physical index} take a Card object which
doesn't require callers to know which identifier to use for each function. There are
four primary methods to creating a Card:
\begin{enumerate}
\item \textbf{pyw.getcard} returns a Card object from a given dev,
\item \textbf{pyw.devinfo} returns the dict info where info['card'] is the Card
object. (This function will take either a card or a ifindex),
\item \textbf{pyw.devadd} returns a new Card object (this function will only take
a phy),
\item \textbf{pyw.ifaces} returns a list of tuples t = (Card,mode) sharing the
same phy as a given dev.
\end{enumerate}
A side affect of using Cards is that many of the netlink calls require the ifindex.
The ifindex is found through the use of ioctl, meaning two sockets have to be
created and two messages have to be sent, received and parsed in order to execute
the command. With Cards, the ifindex is requested for only once. \\
Keep in mind that any identifier (phy, dev, ifindex) can be invalidated outside
of your control. Another program can rename your interface, that is change the
dev without your knowledge. Depending on what functions are being used this may
not be noticed right away as the phy will remain the same. Also for usb devices,
(if the usb is disconnected and reconnected) will have the same dev but the phy
and ifindex will be different.
\subsection{Benchmarks}
PyRIC makes use of several "extensions" to speed up pyw functions:
\begin{enumerate}
\item \textbf{Persistent sockets}: pyw provides the caller with functions and
the ability to pass their own netlink (or ioctl socket) to pyw functions
\item \textbf{One-time request for the nl80211 family id}: pyw stores the family
id in a global variable
\item \textbf{Consolidation} different "reference" values are consolidated in one
class (see the previous section)
\end{enumerate}
\begin{table}
\begin{center}
\begin{tabular}{| l | r | r | r | r|}
\hline
chset & Total & Avg & Longest & Shortest \\
\hline
Popen(iw) & 588.3059 & 0.0588 & 0.0682 & 0.0021 \\
\hline
one-time & 560.3559 & 0.0560 & 0.0645 & 0.0003 \\
\hline
persistent & 257.8293 & 0.0257 & 0.0354 & 0.0004 \\
\hline
\end{tabular}
\caption{Benchmark: Popen(iw) vs pyw}
\end{center}
\label{tab:benchmark}
\end{table}
While small, these changes can improve the performance of any programs using pyw.
Table 1 shows benchmarks for hop time on an Alfa AWUS036NH conducted 10000 times.
Note that we are not implying that PyRIC is faster than iw. Rather, the table
shows that PyRIC is faster than using Popen to execute iw. Using one-time sockets,
there is a difference of 28 seconds over Popen and iw with a small decrease in
the average hoptime. Not a big difference. However, the performance increased
dramatically when persistent netlink sockets are used with the total time and
average hop time nearly halved.
\section{Installing PyRIC}\label{sec:installing}
The easiest way to install PyRIC is through PyPI:\\
\texttt{sudo pip install PyRIC}\\
You can also install PyRIC from source. The tarball can be downloaded from:
\begin{itemize}
\item PyPi: https://pypi.python.org/pypi/PyRIC,
\item PyRIC Web: http://wraith-wireless.github.io/PyRIC, or
\item Github: https://github.com/wraith-wireless/PyRIC.
\end{itemize}
After downloading, extract and run:\\
\texttt{sudo python setup.py install}\\
If you just want to test PyRIC out, download your choice from above. After
extraction, move the pyric folder (the package directory) to your location of
choice and from there start Python and import pyw. It is very important that you
do not try and run it from PyRIC which is the distribution directory. This will
break the imports pyw.py uses. \\
You will only be able to test PyRIC from the pyric directory but, if you want to,
you can add it to your Python path and run it from any program or any location.
To do so, assume you untared PyRIC to /home/bob/PyRIC. Create a text file named
pyric.pth with one line \\
/home/bob/PyRIC \\
and save this file to /usr/lib/python2.7/dist-packages (or
/usr/lib/python3/dist-packages if you want to try it in Python 3).
\begin{table}
\begin{center}
\begin{tabular}{| l | r | r | r |}
\hline
Source & Stability & Recency & Installation \\
\hline
pip & 5 & 3 & 5\\
\hline
PyPI & 5 & 3 & 4\\
\hline
PyRIC Web & 4 & 4 & 4\\
\hline
Github & 3 & 5 & 3\\
\hline
\end{tabular}
\caption{Stability vs Recency vs Installation}
\end{center}
\label{tab:install}
\end{table}
\section{Using PyRIC}\label{sec:using}
As stated previously, PyRIC provides a set of functions to interact with your
system's radio(s) and the ability to interact directly with the kernel through
netlink and ioctl sockets.
\subsection{Interacting with the Wireless Core and Wireless NICs: pyw.py}
If you can use iw, you can use pyw. The easist way to explain how to use pyw is
with an example. Imagine your wireless network, on ch 6, has been experiencing
difficulties lately and you want to capture some traffic to analyse it. Listing
\ref{lst:pentest} shows how to set up a wireless pentest environment. \\
\begin{lstlisting}[caption={Setting up a Wireless Pentest Environment},
label={lst:pentest},
language=Python]
1: import pyric # pyric error (and ecode EUNDEF)
2: from pyric import pyw # for iw functionality
3: from pyric.utils.channels import rf2ch # rf to channel conversion
4:
5: dev = 'wlan0'
6: dinfo = pyw.devinfo(dev)
7: card = dinfo['card']
8:
9: pyw.down(card)
10: pyw.macset(card,'00:03:93:57:54:46')
11:
12: pdev = 'pent0'
13: pcard = pyw.devadd(card, pdev, 'monitor')
14: for iface in pyw.ifaces(card):
15: if iface[0].dev != pcard.dev:
16: pyw.devdel(iface[0])
17: pyw.up(pcard)
18:
19: pyw.chset(pcard,6,None)
20:
21: # DO stuff here
22:
23: card = pyw.devadd(pcard,card.dev,dinfo['mode'])
24: pyw.devdel(pcard)
25: pyw.macset(card,dinfo['mac'])
26: pyw.up(card)
\end{lstlisting}
Listing \ref{lst:pentest} shows basic pyw functions and is the basic shell used in
another project, Wraith\cite{wraith}, to instantiate a wireless (802.11) sensor -
(for a full listing of all pyw functions see Appendix \ref{sec:pywapi}) - with
scanning capabilities. \\
Lines 1 and 2 should always be included as they import the pyric error and pyw
functions. Line 3 imports the rf2ch conversion function. \\
In lines 5 through 10, a Card is created from the device wlan0. The info dict is save
IOT to restore later. Next, the mac address of wlan0 is changed. Note, the device has
to be brought down first. \\
Starting on line 12, a device named 'pent0' is created in monitor mode. First, a new
Card, pcard is create in monitor mode. Then, all interfaces on the same phy are
deleted \footnote{we have found that it is better to delete all interfaces on the
same phy ensuring that external processes don't interfere with the new device}. The
new Card is brought up and set to channel 6 NOHT.\\
Restoring the device starts on line 23, where the virtual interface is deleted,
the previous interface is restored, the mac address is reset and the old Card is
brought up. \\
An extended version of Listing \ref{lst:pentest} can be found in the examples
directory.
\subsubsection{One-time vs Persistent Sockets}
The example in Listing \ref{lst:pentest} uses one-time sockets (netlink and
ioctl). When using iw, there are several things that occur prior to the actual
command or request being submitted. First, iw creates a netlink socket. Then,
iw will request the family id for nl80211. The relative time spent doing this
is neglible but, it is redundant and it may become noticeable in programs that
repeatedly use the Netlink service. Once comlete, iw closes the socket. In some
cases, the ifindex of the device is needed and iw will also initiate an ioctl
call to retrieve it. PyRIC eliminates these redundancies by using a global
variable in pyw that stores the family id after the first time it is requested
and by providing callers the option to use persistent sockets.
\begin{itemize}
\item \textbf{One-time Sockets} Similar to iw. The command, creates the netlink
socket (or ioctl socket), composes the message, sends the message and receives
the response, parses the results, closes the socket and returns the results to
the caller. At no time does the caller need to be aware of any underlying Netlink
processes or structures.
\item \textbf{Persistent Sockets} Communication and parsing only. The onus of
socket creation and deletion is on the caller which allows them to create one
(or more) socket(s). The pyw functions will only handle message construction,
message sending and receiving and message parsing.
\end{itemize}
The caller needs to be cognizant of whether the function requires a netlink or
ioctl socket. Passing the wrong type will result in an error. \\
NOTE: One must remember that there is an upper limit to the number of open netlink
sockets. It is advised to use one-time functions as much as possible and save the
use of persistent sockets for use in code that repeatedly makes use of netlink. \\
The latest version of pyw.py (v 0.1.*) implements this functionality through the
use of what I call templates\footnote{I use templates and stubs for the lack
of any better naming convention}, Listing \ref{lst:template} and stubs Listing
\ref{lst:stub}.
\begin{lstlisting}[caption={A Basic Netlink Function Template},
label={lst:template},
language=Python]
def fcttemplate(arg0,arg1,..,argn,nlsock=None):
# put parameter validation (if any) here
if nlsock is None: _nlstub_(fcttemplate,arg0,arg1,...,argn)
# command execution
...
return results
\end{lstlisting}
The template function in Listing \ref{lst:template} checks if nlsock is instantiatd
\footnote{ioctl calls operate in the same manner}. If so, it proceeds to execution.
If there is no socket, the stub is executed which creates one. If something other
than a netlink socket is at argv[0], an error will be raised during execution. \\
\begin{lstlisting}[caption={Function \_nlstub\_},
label={lst:stub},
language=Python]
def _nlstub_(fct,*argv):
nlsock = None
try:
nlsock = nlsock = nl.nl_socket_alloc()
argv = list(argv) + nlsock=None
return fct(*argv)
except pyric.error:
raise # catch & release
finally:
if nlsock: nl.nl_socket_free(nlsock)
\end{lstlisting}
The stub function, Listing \ref{lst:stub} allocates a netlink socket, executes
the original (now with a netlink socket) and then destroys the netlink socket.\\
\begin{lstlisting}[caption={Using Persistent Sockets},
label={lst:persistent},
language=Python]
1: import pyric # pyric errors
2: from pyric import pyw # for iw functionality
3: from pyric.lib import libnl as nl # for netlink sockets
4:
5: nlsock = nl.nl_socket_alloc(timeout=1)
6: card = pyw.getcard('wlan0',nlsock)
7: print pyw.devmodes(card,nlsock)
8: nl.nl_socket_free(nlsock)
\end{lstlisting}
Listing \ref{lst:persistent}, shows the creation of a persistent netlink socket
that is used in the creation of a card and in retrieved the card's supported
modes. \\
Use Python's built in help features on pyw functions or see Appendex \ref{sec:pywapi}
to determine what type of socket is needed.
\subsection{Additional Tools}
In the utils directory, PyRIC includes channels.py, hardware.py, rfkill.py and
ouifetch.py. These provide a port of rfkill, channel/frequency enumeration and
device chipset, driver retrieval as well as some mac address functions. More
information can be found in the Appendices and in README.md.
\subsection{Interacting with the Kernel: libnl.py and libio.py}
The kernel interfaces, libnl.py and libio.py are located in the lib directory.
They handle socket creation/deletion, message creation/parsing and kernel
communication. Aside from creating and deleting persistent sockets, there is
little need to access their functions unless you plan on extending pyw
functionality. As such, a further discussion of libnl.py and libio.py can be
found in the next section.
\section{Extending PyRIC}\label{sec:extending}
You may find that pyw does not offer some of the functionality you need. Using
libnl.py and/or libnl.io, additional functionality can be added to your program.\\
It is helpful if the reader has a basic knowledge of netlinks. For a review, see
"Communicating between the kernel and user-space in Linux using Netlink Sockets"
\cite{spae}.
\subsection{Porting C}
All Python ports of C header files can be found in the net directory. C Enums
and \#defines are ported using constants. C structs are ported using three
Python structures and the Python struct package:
\begin{enumerate}
\item a format string for packing and unpacking the struct
\item a constant specifying the size of the struct in bytes
\item a function taking the attributes of the struct as arguments and returning
a packed string
\end{enumerate}
Listing \ref{lst:cstruct} shows the C definition of the nlmsghdr found in netlink.h.
\begin{lstlisting}[caption={C Struct nlmsghdr},
label={lst:cstruct},
language=C]
struct nlmsghdr {
__u32 nlmsg_len;
__u16 nlmsg_type;
__u16 nlmsg_flags;
__u32 nlmsg_seq;
__u32 nlmsg_pid;
};
\end{lstlisting}
And Listing \ref{lst:pstruct} shows the ported version in Python.
\begin{lstlisting}[caption={Corresponding Python Definition},
label={lst:pstruct},
language=Python]
nl_nlmsghdr = "IHHII"
NLMSGHDRLEN = struct.calcsize(nl_nlmsghdr)
def nlmsghdr(mlen,nltype,flags,seq,pid):
return struct.pack(nl_nlmsghdr,NLMSGHDRLEN+mlen,nltype,flags,seq,pid)
\end{lstlisting}
When using pyw, dealing with these structures is handled transparently by libnl.py
and libio.py. When extending or customizing pyw, a basic understanding of the
definitions in netlink\_h.py, genetlink\_h.py and if\_h.py.
\subsection{Input/Output Control (ioctl)}
PyRIC provides more than just iw-related functions, it also implements functions
from ifconfig and iwconfig. These command line tools still use ioctl (or the proc
directory). For example, interfaces() reads from '/proc/net/dev' to retrieve all
system interfaces and winterfaces() use ioctl to check if a device is wireless.
Input/Output control calls have only been used when there was no viable alternative
and, it should not be necessary to have to add any further ioctl commands. If you
find that you need an ioctl related command, search through if\_h.py for the
appropriate structure and add it's definitions to ifreq.
\subsection{Netlink and nl80211}
Documentation on Netlink, and nl80211 in particular, is so minimal as to be
neglible. The clusterfuck of code and lack of comments in the iw source tree
make it impossible to use as any sort of roadmap. Fortunately Thomas Graf's
site\cite{libnl} has excellent coverage of libnl, the Netlink library. Using
this as a reference, a simple Netlink parser was put together which later became
libnl.py. Using the command line tool strace and libnl.py, Netlink messages could
be dissected and analyzed.\\
Let us consider adding a virtual interface with the command:\\
\texttt{sudo iw phy0 interface add test0 type monitor}\\
First, we need to see what is going on under the covers. Using strace:\\
\texttt{strace -f -x -s 4096 iw phy0 interface add test0 type monitor}\\
from a terminal will give a you a lot of output, most irrelevant (to us). Scroll
through this until the netlink socket creation as highlighted in Figure
\ref{fig:nlsock}. You can see that a socket of type PF\_NETLINK is created and
the send/receive buffers are set to 32768.
\begin{center}
\begin{figure}[h]
\includegraphics{nlsock}
\caption{Netlink socket creation}
\label{fig:nlsock}
\end{figure}
\end{center}
What we want to analyze are the messages sent and received over the netlink
socket. In Figure \ref{fig:nlsock}, iw is requesting the family id for nl80211.
This id will be used in subsequent requests related to nl80211 as we will see
shortly. The return message gives the nl80211 family id as 26 and returns other
nl80211 attributes. This is handled by the private function \_familyid\_ in
pyw.py.
Figure \ref{fig:nlsend} shows the add interface message being sent to the kernel.
\begin{center}
\begin{figure}[h]
\includegraphics{nlsend}
\caption{Netlink sendmsg}
\label{fig:nlsend}
\end{figure}
\end{center}
We are interested in the byte sequence following msg\_iov(1). Copy this and paste
into in a python variable as in Listing \ref{lst:nlparse} and pass it to the
function nlmsg\_fromstream which parses the byte stream and returns the GENLMsg.\\
\begin{lstlisting}[caption={Parsing netlink messages},
label={lst:nlparse},
language=Python]
>>> from pyric.lib import libnl as nl
>>> sent = "\x30\x00\x00\x00\x1a...\x00\x00"
>>> msg = nl.nlmsg_fromstream(sent)
>>> msg
nlmsghdr(len=48,type=26,flags=5,seq=1463268720,pid=10982)
genlmsghdr(cmd=7)
attributes:
0: type=1,datatype=3
value=0
1: type=4,datatype=5
value=test0
2: type=5,datatype=3
value=6
\end{lstlisting}
The first thing to notice is nlmsghdr type = 26, which of course is nl80211 family
id. The rest of the nlmsghdr components len, flags, seq, and pid are handled by
libnl.py although you can supply your own flags if desired. At this time, you can
manually look up what values the cmd, type and datatype correspond to in
nl80211\_h.py and netlink\_h.py or you can use the tools provided in nlhelp.py.\\
\begin{lstlisting}[caption={Parsing netlink messages continued},
label={lst:nlparse2},
language=Python]
>>> from pyric.net.netlink_h import NLA_DATATYPES
>>> from pyric.docs import nlhelp
>>> nlhelp.cmdbynum(7)
u'@NL80211_CMD_NEW_INTERFACE'
>>>
>>> for attr in msg.attrs:
... print nlhelp.attrbynum(attr[0]), NLA_DATATYPES[attr[2]], attr[1]
...
@NL80211_ATTR_WIPHY u32 0
@NL80211_ATTR_IFNAME string test0
@NL80211_ATTR_IFTYPE u32 6
>>>
>>> from pyric.net.wireless.nl80211_h import NL80211_IFTYPES
>>> NL80211_IFTYPES[6]
'monitor'
\end{lstlisting}
In Listing \ref{lst:nlparse2} command number 7 corresponds to
NL80211\_CMD\_NEW\_INTERFACE and the attributes that need to be passed to the
kernel are NL80211\_ATTR\_WIPHY, NL80211\_ATTR\_IFNAME and NL80211\_ATTR\_IFTYPE.
The IFTYPE is also known as the mode i.e. 'monitor' which can be found in
nl80211\_h.py NL80211\_IFTYPES. We don't parse the return message from the kernel
but, it follows the same SOP. In this case, it returns the attributes of the new
virtual interface. \\
With this information, we can now code our function. Recall the fcttemplate as
defined in Listing \ref{lst:template} and fill in the command execution as shown
in Listing \ref{lst:coding}. \\
\begin{lstlisting}[caption={Coding the function},
label={lst:coding},
language=Python]
# construct the message
msg = nl.nlmsg_new(nltype=_familyid_(nlsock),
cmd=nl80211h.NL80211_CMD_NEW_INTERFACE,
flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK)
nl.nla_put_u32(msg,card.phy,nl80211h.NL80211_ATTR_WIPHY)
nl.nla_put_string(msg,vdev,nl80211h.NL80211_ATTR_IFNAME)
nl.nla_put_u32(msg,IFTYPES.index(mode),nl80211h.NL80211_ATTR_IFTYPE)
# send, receive and parse return results, returning the new Card
nl.nl_sendmsg(nlsock,msg)
rmsg = nl.nl_recvmsg(nlsock) # success returns new device attributes
return Card(card.phy,vdev,nl.nla_find(rmsg,nl80211h.NL80211_ATTR_IFINDEX))
\end{lstlisting}
We construct a new GENLMsg passing the nl80211 family id, the command we got
earlier and flags specifying that this is a request and we want to get an ACK
back\footnote{libnl.py always forces an ACK and handles the underlying process
of receiving it}. Now, add each attribute to the message. Note the order: value,
then attribute. With the message constructed, send it to the kernel, get the
results, parse and return them.\\
Rather simple, in fact the hardest part is figuring out what to send to the kernel.
Everything else is handled behind the scenes by libnl.py.
\begin{appendices}
\section{API: pyw.py}\label{sec:pywapi}
\subsection{Constants}
\begin{itemize}
\item \textbf{\_FAM80211ID\_}: Global netlink family id of nl80211. Do not touch
\item \textbf{IFTYPES}: redefined (from nl80211\_h.py) interface modes
\item \textbf{MNTRFLAGS}: redefined (from nl80211\_h.py) monitor mode flags
\item \textbf{TXPOWERSETTINGS}: redefined (from nl80211\_h.py) power level settings
\end{itemize}
\subsection{Objects/Classes}
\textbf{Card} A wrapper around a tuple
\texttt{t = (physical index,device name,interface index)}
which exposes the following properties through '.':
\begin{itemize}
\item \textbf{phy}: physical index
\item \textbf{dev}: device name
\item \textbf{idx}: interface index (ifindex)
\end{itemize}
Because the underlying Netlink calls will sometimes require the physical index,
sometimes the device name, and sometimes the ifindex, pyw functions accept a Card,
object. This allows callers to use pyw functions without having to remember which
identifier the function requires. However, in some cases the function requires
a dev or accepts both. See the next section on functions.\\
While callers could create their own Cards, it is recommend to use one of the
following
\begin{itemize}
\item \textbf{pyw.getcard} returns a Card object from a given dev
\item \textbf{pyw.devinfo} returns the dict info where info['card'] is the Card
object. This function will take either a card or a dev
\item \textbf{pyw.devadd} returns a new Card object
\item \textbf{pyw.devadd} returns a new Card object
\item \textbf{pyw.ifaces} returns a list of tuples t = (Card,mode) sharing the
same phy as a given device to do so. It is also recommended to periodically
validate the Card. On some cheaper usb wireless nics, there are periodic
disconnects which results in a new phy and ifindex.
\end{itemize}
\subsection{Functions}
\begin{itemize}
\item interfaces(): (ifconfig), type: filesystem, returns list of all network
device names
\item isinterface(dev): (ifconfig <dev>) type: filesystem, checks that dev is a
device name
\item winterfaces(iosock=None): (iwconfig), type: ioctl, list wireless device
names
\item iswireless(dev,iosock=None): (iwconfig <dev>), type: ioctl, check dev is a
wireless interface
\item phylist(): (iw phy | grep wiphy) type: N/A, list phy indexes and phy names
present on system
\item regget(nlsock=None: (iw reg get), type: netlink, get regulatory domain
\item regset(rd,nlsock=None): (iw reg set <rd>), type: netlink, set regulatory domain
to rd
\item getcard(dev,nlsock=None) (N/A), type: hybrid netlink and ioctl: get a Card
object for dev
\item validcard(card,nlsock=None): (N/A), type: (hyrbrid netlink and ioctl), verify
card is still valid
\item macget(card,iosock=None): (ifconfig), type: ioctl, determine if card is up
or down
\item macset(card,mac,iosock=None): (ifconfig card.<dev> hw ether <mac>), type:
ioctl, set card's hw address to mac
\item isup(card,iosock=None): (ifconfig card.<dev>)
\item up(card,iosock=None) (ifconfig card.<dev> up), type: ioctl, bring card up
\item down(card,iosock=None): (ifconfig card.<dev> down), type: ioctl, bring card
down
\item isblocked(card): (rfkill list <rfkill\_idx>): type N/A returns tuple
(Soft Block State, Hard Block State)
\item block(card): (rfkill block <rfkill\_idx>) type: N/A, soft blocks card
\item unblock(card): (rfkill unblock <rfkill\_idx>) type: N/A, removes the soft
block on card
\item pwrsaveget(card,nlsock=None) (iw dev card.<dev> get power\_save) type: netlink
get card's power save state True = on, False = off
\item pwrsaveset(card,on,nlsock=None) (iw dev card.<dev> set power\_save <on>) type:
netlink, set card's power save state True = on, False = off
\item covclassget(card,nlsock=None) (iw phy card.<phy> get coverage <cc>) type:
netlink get card's coverage class
\item covclassset(card,cc,nlsock=None) (iw phy card.<phy> set coverage <cc>) type:
netlink set card's coverage class
\item retryshortget(card,nlsock=None) (iw phy card.<phy> info | grep 'retry short')
type:netlink get card's retry short limit
\item retryshortset(card,lim,nlsock=None) (iw phy card.<phy> set retry short <lim>)
type:netlink set card's retry short limit. NOTE: although 255 is specified as the
max limit for this and the long retry, kernel v4 will not allow it.
\item retrylongget(card,nlsock=None) (iw phy card.<phy> info | grep 'retry long')
type:netlink get card's retry long limit
\item retrylongset(card,lim,nlsock=None) (iw phy card.<phy> set retry long <lim>)
type:netlink set card's retry long limit
\item rtsthreshget(card,nlsock=None) (iw phy card.<phy> info | grep rts) type:
netlink set card's RTS threshold
\item rtsthreshset(card,thresh,nlsock=None) (iw phy card.<phy> set rts <thresh>) type:
netlink set card's RTS threshold
\item fragthreshget(card,nlsock=None) (iw phy card.<phy> info | grep frag) type:
netlink get card's fragmentation threshold
\item fragthreshset(card,thresh,nlsock=None) (iw phy card.<phy> set frag <thresh>) type:
netlink set card's fragmentation threshold
\item ifaddrget(card,iosock=None): (ifconfig card.<dev>), type: ioctl, get ip4 address,
netmask and broadcast address of card
\item ifaddrset(card,ipaddr,netmask,broadcast,iosock=None): (ifconfig card/<dev>
<ipaddr> netmask <netmask> broadcast <broadcast>), type: ioctl, set the interface
addresses of the card
\item inetset(card,ipaddr,iosock=None): (ifconfig card.<dev> <ipaddr>), type: ioctl,
set the card's ip4 address
\item maskset(card,netmask,iosock=None): (ifconfig card.<dev> netmask <netmask>),
type: ioctl, set the card's netmask
\item bcastset(card,broadcast,iosock=None): (ifconfig card.<dev> broadcast
<broadcast>), type: ioctl, set the card's broadcast address
\item devfreqs(card,nlsock=None): (iw phy card.phy info), type: netlink, get card's
supported frequencies
\item devchs(card,nlsock=None): (iw phy card.phy info), type: netlink, get card's
supported channels
\item devstds(card,nlsock=None): (iwconfig card.<dev> | grep IEEE), type: nlsock,
returns a list of card's 802.11 supported standards by letter designator
\item devmodes(card,nlsock=None): (iw phy card.phy info | grep interface), type:
netlink, get card's supported modes
\item devcmds(card,nlsock=None): (iw phy card.phy info | grep commands), type:
netlink, get card's supported commands
\item ifinfo(card,iosock=None): (ifconfig card.<dev>), type: ioctl, get hardware
related info for card
\item devinfo(card,nlsock=None): (iw dev card.<dev> info), type: netlink, get info
for dev
\item phyinfo(card,nlsock=None): (iw phy card.<phy> info), type: netlink, get info
for phy
\item ifaces(card,nlsock=None): (APX iw card.dev | grep phy\#), type: netlink, get all
cards (w/ modes) of interfaces sharing the same phy as card
\item txset(card,pwr,lvl,nlsock=None) (iw phy phy0 set txpower <lvl> <pwr>), type:
netlink,sets the tx power to pwr (in dBm) with level setting lvl
\item txget(card,iosock=None): (iwconfig card.<dev> | grep Tx-Power card), type:
ioctl, get card's transmission power
\item chget(card,nlsock=None): (iw dev <card.dev> info | grep channel), type:
netlink, get card's current channel
\item chset(card,ch,chw=None,nlsock=None): iw phy <card.phy> set channel <ch> <chw>),
type: netlink, set card's current channel to ch with width chw
\item freqget(card,nlsock=None): (iw dev <card.dev> info | grep channel), type:
netlink, get card's current frequency
\item freqset(card,rf,chw=None,nlsock=None): iw phy <card.phy> set freq <rf> <chw>),
type: netlink, set card's current frequency to rf with width chw
\item devmodes(card,iosock=None): (iw phy card.<phy>), type: netlink, get modes
supported by card
\item modeset(card,mode,flags=None,nlsock=None): (iw dev card.<dev> set type <mode>
[flags]), type: netlink, set card's mode to mode with flags (if mode is monitor)
\item modeget(card,nlsock=None): (iw dev card.<dev> info | grep mode), type: netlink,
get card's mode
\item devset(card,ndev,nlsock=None): (N/A) sets the dev (name) of card to ndev
\item phyadd(phy,vnic,mode,flags=None,nlsock=None): (iw phy <phy> interface add <vnic>
type <mode> flags <flags>)\footnote{There is a bug in some kernel v4.4.0-x
where the given dev name is ignored and a system chosen one is used instead. See
https://wraithwireless.wordpress.com. Whenever possible, use devadd to create
interfaces instead.}, type: netlink, creates a new virtual interface with dev vdev,
in mode and using flags. Note: flags are only supported when creating a monitor mode
\item devadd(card (or ifindex),vnic,mode,[flags],nlsock=None): (iw phy card.<dev>
interface add <vnic> type <mode> flags <flags>), type: netlink, creates a new virtual
interface with dev vdev, in mode and using flags. Note: flags are only supported
when creating a monitor mode. This function accepts either a Card object or a
ifindex.
\item devdel(card,nlsock=None): (iw card.<dev> del), type: netlink, deletes card
\begin{itemize}
\item isconnected(card,nlsock=None): (iw dev card.<dev> info | grep channel), type:
netlink, determines if card is connected
\item connect(card,ssid,bssid=None,rf=None,nlsock=None): (iw dev card.<dev> connect <ssid>
<rf> <bssid>) connects to AP SSID with BSSID
\item disconnect(card, nlsock=None): (iw dev card.<dev> disconnect), type: netlink,
disconnects card from AP
\item link(card, nlsock=None): (iw dev card.<dev> link), type: netlink, displays
link specific details, i.e. AP details that card is connected to
\item stainfo(card, mac, nlsock=None): (iw dev card.<dev> link) type: netlink, displays
tx, rx metrics of the AP that card is connected to
\item \_hex2mac\_(v): returns a ':' separated mac address from byte stream v
\item \_mac2hex\_(v): returns a hex string corresponding to mac address v
\item \_hex2ip4\_(v): returns a '.' separated ip4 address from byte stream v
\item \_validip4\_(addr): determines if addr is a valid ip4 address
\item \_validmac\_(addr): determines if addr is a valid mac address
\item \_issetf\_(flags,flag): determines if flag is set in flags
\item \_setf\_(flags,flag): set flag in flags to on
\item \_unsetf\_(flags,flag): set flag in flags to off
\item \_familyid\_(nlsock): returns and sets the Netlink family id for nl80211,
only called once per module import
\item \_ifindex\_(dev,iosock=None): returns dev's ifindex
\item \_flagsget\_(dev,iosock=None): get's the dev's interface flags
\item \_flagsset\_(dev,flags,iosock=None): set's the dev's interface flags
\item \_iftypes\_(i): returns the mode corresponding to i
\item \_bands\_(band): futher parses band attribute returns dict of bands
containting rf information and rate information
\item \_band\_rates\_(rs): extracts list of rates from the unpacked rates rs
\item \_band\_rfs\_(rfs): extracts list of RFs (and other data) from the unpacked
frequencies rfs
\item \_unparsed\_rfs\_(band): (legacy) returns a list of frequencies from the
unparsed byte string band
\item \_commands\_(command): converts the list of numeric commands to a list of
commands as strings
\item \_ciphers\_(cipher): returns a list of ciphers from the packed byte string
cipher
\item \_rateinfo\_(ri): returns parsed rate info from the packed byte string ri
\item \_iostub\_(fct,*argv): ioctl stub function, calls fct with parameter list
argv and an allocated ioctl socket
\item \_nlstub\_(fct,*argv): netlink stub function, calls fct with parameter list
argv and an allocated netlink socket
\end{itemize}
\end{itemize}
\section{API: channels.py}\label{sec:channels.api}
Channel, Frequency enumeration and conversions can be found in channels.py.
\subsection{Constants}
\begin{enumerate}
\item \textbf{CHTYPES}: imported channel types from nl80211\_h
\item \textbf{CHWIDTHS}: imported channel widths from nl80211\_h
\item \textbf{ISM\_24\_C2F}: Dict containing ISM channel (key) to frequency (value)
pairs
\item \textbf{ISM\_24\_F2C}: Dict containing ISM frequency (key) to channel (value)
pairs
\item \textbf{UNII\_5\_C2F}: Dict containing UNII 5Ghz channel (key) to frequency
(value) pairs
\item \textbf{UNII\_5\_F2C}: Dict containing UNII 5Ghz frequency (key) to channel
(value) pairs
\item \textbf{UNII\_4\_C2F}: Dict containing UNII upper 4Ghz channel (key) to
frequency (value) pairs
\item \textbf{UNII\_4\_F2C}: Dict containing UNII upper 4Ghz frequency (key) to
channel (value) pairs
\end{enumerate}
\subsection{Functions}
\begin{enumerate}
\item channels(): returns a list of all channels
\item freqs(): returns a list of all frequencies
\item ch2rf(c): convert channel c to frequency
\item rf2ch(f): convert frequency f to channel
\end{enumerate}
\section{API: hardware.py}\label{sec:hardwareapi}
Hardware related: driver, chipset, manufacturer and mac address utility functions
can be found in device.py.
\subsection{Constants}
\begin{enumerate}
\item \textbf{dpath}: path to system device details
\item \textbf{drvpath}: path to device drivers
\end{enumerate}
\subsection{Functions}
\begin{enumerate}
\item oui(mac): returns the oui portion of address <mac>
\item ulm(mac): returns the ulm portion of address <mac>
\item manufacturer(ouis,mac): returns the manufacturer name of <mac> given the
dict of <ouis>
\item randhw([ouis]): returns a random mac address. If the dict ouis is specified
will select a random oui from the dict otherwise will generate one
\item ifcard(dev): returns the device driver and chipset
\item ifdriver(dev): returns the device driver
\item ifchipset(driver): returns the chipset associated with driver
\end{enumerate}
\section{API: ouifetch.py}
The file ouifetch.py retrieves and saves a tab seperated file of oui to
manufacturer name for use by hardware.py functions. From a command line, type: \\
\subsection{Constants}
\begin{enumerate}
\item \textbf{OUIURL}: url of IEEE oui file
\item \textbf{OUIPATH}: path to default location PyRIC oui.txt file
\end{enumerate}
\subsection{Functions}
\begin{enumerate}
\item load([opath]): returns a dict of oui:manufacturer key->value pairs stored
in the text file at opath. If opath is not specified, uses the default
\item fetch([opath]): retrieves oui.txt from the IEEE website, parses the files
and stores the results in a PyRIC friendly format in opath. If opath is not
specified, uses the default. User must have root permissions in order to write
to default opath
\end{enumerate}
\section{API: rfkill.py}\label{sec:rfkillapi}
A port of the command line tool rfkill, rfkill.py writes and reads rfkill\_event
structures to /dev/rfkill using fcntl providing functionality to block and unblock
devices.
\subsection{Constants}
\begin{enumerate}
\item \textbf{RFKILL\_STATE}: list of boolean values corresponding to blocked,
unblocked
\end{enumerate}
\subsection{Functions}
\begin{enumerate}
\item rfkill\_list(): corresponds to rkill list, returns a dict of dicts name ->
\{idx, type, soft, hard\}. If type is 'wireless', then name will be of the form
phy<n> such that n is the physical index of the wireless card
\item rfkill\_block(idx): soft blocks the device at rfkill index idx
\item rfkill\_blockby(rtype): soft blocks all devices of type rtype
\item rfkill\_unblock(idx): turns off the soft block at rfkill index idx
\item rfkill\_unblockby(rtype): turns off the soft blocks of all devices of type
rtype
\item soft\_blocked{idx}: determines soft block state of device at rfkill index idx
\item hard\_blocked{idx}: determines hard block state of device at rfkill index idx
\item getidx(phy): returns the rfkill index of the device with physical index phy
\item getname(idx): returns the name of the device at rfkill index idx
\item gettype(idx): returns the type of the device at rfkill index idx
\end{enumerate}
\section{API: libnl.py}\label{sec:libnlapi}
Providing libnl similar functionality, libnl.py provides the interface between
pyw and the underlying nl80211 core. It relates similarily to libnl by providing
functions handling netlink messages and sockets and where possible uses similarly
named functions as those libnl to ease any transitions from C to PyRIC. However,
several liberties have been taken as libnl.py handles only nl80211 generic netlink
messages.
\subsection{Constants}
\begin{itemize}
\item \textbf{BUFSZ} default rx and tx buffer size
\end{itemize}
\subsection{Classes/Objects}
The two classes in libnl.py, NLSocket and GENLMsg, discussed in the following
sections subclass Python's builtin dict. This has been done IOT to take advantage
of dict's already existing functions and primarily their mutability and Python's
'pass by name' i.e. modifications in a function will be reflected in the caller.
This makes the classes very similar to the use C pointers to structs in libnl.
\subsubsection{NLSocket}
NLSocket is a wrapper around a netlink socket which exposes the following
properties through '.':
\begin{itemize}
\item \textbf{sock}: the actual socket
\item \textbf{fd}: the socket's file descriptor (deprecated)
\item \textbf{tx}: size of the send buffer