root/UMPA/branch/link-layer-integration/umit/umpa/_sockets.py @ 5594

Revision 5594, 7.0 kB (checked in by kosma, 3 years ago)

platform check: detect MS Windows via os.name

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2008-2010 Adriano Monteiro Marques.
5#
6# Authors: Bartosz SKOWRON <getxsick at gmail dot com>
7#          Kosma Moczek <kosma at kosma dot pl>
8#
9# This library is free software; you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published
11# by the Free Software Foundation; either version 2.1 of the License, or
12# (at your option) any later version.
13#
14# This library is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17# License for more details.
18#
19# You should have received a copy of the GNU Lesser General Public License
20# along with this library; if not, write to the Free Software Foundation,
21# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23"""
24Raw sockets support.
25
26Contains Socket classes which can be used to send raw packets in
27a platform-independent manner. The standard Python socket module can be used
28instead if advanced functionalities are needed.
29"""
30
31import socket
32import sys
33import os
34from fcntl import ioctl
35
36from umit.umpa.utils.exceptions import UMPAException, UMPANotPermittedException
37
38# constants from various header files not available under Python
39ETH_P_ALL = 3                     # from linux/if_ether.h
40BIOCSETIF = 2149597804            # from net/bpf.h
41
42# Detect socket programming model. This greatly simplifies socket code.
43if sys.platform == 'linux2':
44    _l2model = 'AF_PACKET'
45    _l3model = 'AF_INET'
46    _l3quirk = None
47elif sys.platform.startswith('freebsd') or \
48     sys.platform.startswith('netbsd') or \
49     sys.platform.startswith('darwin'):
50    _l2model = 'bpf'
51    _l3model = 'AF_INET'
52    _l3quirk = 'ntohs'
53elif sys.platform.startswith('openbsd'):
54    _l2model = 'bpf'
55    _l3model = 'AF_INET',
56    _l3quirk = None
57elif os.name == 'nt':
58    _l2model = 'NDIS'
59    _l3model = 'AF_INET'
60    _l3quirk = 'windows'
61else:
62    _l2model = None
63    _l3model = None
64    _l3quirk = None
65
66def send(*packets, **kwargs):
67    """
68    Send arbitrary packets.
69
70    The function creates sockets of proper level as needed. The 'iface'
71    named argument must be supplied for L2 (link-layer) sockets.
72
73    @type packets: C{Packet}
74    @param packets: list of umit.umpa.Packet objects to send.
75
76    @returns: List of return values (byte counts) from the send() function.
77    """
78
79    sent_bytes = []
80    for packet in packets:
81        # create appropriate socket based on packet's lowermost layer
82        if packet.protos[0].layer == 2:
83            sock = SocketL2(iface=kwargs.get('iface'))
84        else:
85            sock = SocketL3()
86        sent_bytes.extend(sock.send(packet))
87    return sent_bytes
88
89class SocketL2(object):
90    """
91    Level 2 (link-layer) socket class.
92
93    Supported platforms: Linux (AF_PACKET), BSD (bpf).
94    """
95
96    def __init__(self, iface=None):
97        """
98        Create a new SocketL2.
99
100        @type iface: C{str}
101        @param iface: Interface to use for sending the packets.
102        """
103        if iface is None:
104            # TODO: port interface detection from the link-layer branch
105            raise NotImplementedError("You need to specify iface")
106
107        if _l2model == 'AF_PACKET':
108            try:
109                self._sock = socket.socket(socket.AF_PACKET,
110                        socket.SOCK_RAW,
111                        ETH_P_ALL)
112            except socket.error, msg:
113                raise UMPANotPermittedException(msg)
114
115            self._sock.bind((iface, ETH_P_ALL))
116            self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**20)
117        elif _l2model == 'bpf':
118            try:
119                self._sock = open("/dev/bpf", 'w')
120            except IOError, msg:
121                raise UMPAException('cannot open /dev/bpf: ' + msg)
122
123            ioctl(self._sock, BIOCSETIF, iface)
124        else:
125            raise NotImplementedError("L2 sockets unsupported on your platform")
126
127    def send(self, *packets):
128        """
129        Send packets through the socket.
130
131        @type packets: C{Packet}
132        @param packets: List of umit.umpa.Packet objects to send.
133
134        @returns: List of return values (byte counts) from the send() function.
135        """
136
137        sent_bytes = []
138        for packet in packets:
139            if _l2model == 'AF_PACKET':
140                sent_bytes.append(self._sock.send(packet.get_raw()))
141            elif _l2model == 'bpf':
142                sent_bytes.append(self._sock.write(packet.get_raw()))
143            else:
144                raise NotImplementedError("L2 send unsupported on your platform")
145        return sent_bytes
146
147# TODO: legacy L3 code below, not integrated yet
148
149class Socket(object):
150    """
151    This class handles with sockets.
152
153    To send built packets your need to create a socket.
154    You can use socket module from Python Standard Library directly
155    but it's recommended to use this class instead.
156
157    That is because there are some other features,
158    and for some security issues.
159    """
160
161    def __init__(self):
162        """
163        Create a new Socket().
164        """
165
166        # to create socket object we need root priviligies.
167        # if non-root EUID, then exception is raised
168        # use umit.umpa.utils.security.super_priviliges() to avoid exception
169        # when a new Socket object is created
170        try:
171            self._sock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
172                                                            socket.IPPROTO_RAW)
173        except socket.error, msg:
174            raise UMPANotPermittedException(msg)
175
176        # to build own headers of IP
177        self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
178
179    def send(self, *packets):
180        """
181        Send packets in to the network.
182
183        @type packets: C{Packet}
184        @param packets: packets which were built by umit.umpa.Packet objects.
185        """
186
187        sent_bits = []
188        for packet in packets:
189            # XXX try to use similar mechanism as is for SocketL2
190            # to pick up correct interface, using bind() and send()
191            # then there is no need to pick out a destination address
192            dst_addr = self._get_address(packet)
193            # if dst_addr is a tuple, convert it to a string; works only for IPv4
194            if type(dst_addr) is tuple:
195                dst_addr = ".".join(str(y) for y in dst_addr)
196            sent_bits.append(self._sock.sendto(packet.get_raw(),
197                                                            (dst_addr, 0)))
198        return sent_bits
199
200    def _get_address(self, packet):
201        """
202        Pick out the destination address from 3rd layer.
203
204        @return: destination address from 3rd layer of OSI model.
205        """
206        for proto in packet.protos:
207            if proto.layer == 3:    # XXX: if we included more than one protocol
208                break               #   of layer 3 we got IP from the first one
209
210        if not proto:
211            raise UMPAException("There is not prototocol from 3rd layer.")
212
213        return proto.dst
Note: See TracBrowser for help on using the browser.