You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/lib/utils/ip.py

507 lines
15 KiB

9 years ago
# -*- coding: utf-8 -*-
# Copyright 2008-2016 Mir Calculate. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
9 years ago
from files import (process, checkUtils, readFile, listDirectory,
getRunCommands, getProgPath)
import sys
import os
import re
9 years ago
import struct
import fcntl
import socket
import math
import ctypes
from os import path
9 years ago
import select
import time
from calculate.lib.cl_lang import setLocalTranslate
9 years ago
_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])
SYSFS_NET_PATH = "/sys/class/net"
PROCFS_NET_PATH = "/proc/net/dev"
# From linux/sockios.h
SIOCGIFINDEX = 0x8933
9 years ago
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914
SIOCSIFHWADDR = 0x8924
SIOCSIFADDR = 0x8916
SIOCSIFNETMASK = 0x891C
SIOCETHTOOL = 0x8946
SIOCGIFADDR = 0x8915
SIOCGIFNETMASK = 0x891B
SIOCGIFHWADDR = 0x8927
# Resources allocated
IFF_RUNNING = 0x40
IFF_MASTER = 0x400
IFF_SLAVE = 0x800
# ip digit from 0|1-255|254 (template)
IP_DIG = "[%s-9]|(?:1[0-9]|[1-9])[0-9]|2[0-4][0-9]|25[0-%s]"
# ip net 0-32
IP_NET_SUFFIX = "[0-9]|[12][0-9]|3[012]"
# ip digs 1-254,0-254,0-255
9 years ago
IP_DIGS = {'dig1_254': IP_DIG % (1, 4), 'dig0_254': IP_DIG % (0, 4),
'dig0_255': IP_DIG % (0, 5), }
# ip addr 10.0.0.12
9 years ago
IP_ADDR = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig1_254)s)" % \
IP_DIGS
IP_MASK = "(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)\.(%(dig0_255)s)" % \
IP_DIGS
# ip addr for net 10.0.0.0
9 years ago
IP_NET = "(%(dig1_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)\.(%(dig0_254)s)" % \
IP_DIGS
# ip and net 192.168.0.0/16
9 years ago
IP_ADDR_NET = "(%(ipaddr)s)/((%(ipnet)s))" % {'ipaddr': IP_NET,
'ipnet': IP_NET_SUFFIX}
reIp = re.compile("^{0}$".format(IP_ADDR))
reNetSuffix = re.compile("^{0}$".format(IP_NET_SUFFIX))
reNet = re.compile("^{0}$".format(IP_ADDR_NET))
reMask = re.compile("^{0}$".format(IP_MASK))
9 years ago
def checkIp(ip):
"""Check ip"""
return reIp.match(ip)
9 years ago
def checkNetSuffix(netSuffix):
"""Check net suffix"""
return reNetSuffix.match(netSuffix)
9 years ago
def checkNet(net):
"""Check net"""
if not reNet.match(net):
return False
9 years ago
ip, op, cidr = net.partition('/')
mask = strIpToIntIp(cidrToMask(int(cidr)))
9 years ago
return (strIpToIntIp(ip) & mask) == (strIpToIntIp(ip))
maskDigs = map(lambda x: str(x),
(0b10000000, 0b11000000, 0b11100000, 0b11110000,
0b11111000, 0b11111100, 0b11111110, 0b11111111))
def checkMask(mask):
"""Check net"""
if not mask:
return False
if mask.count('.') != 3:
return False
zero = False
for dig in mask.split('.'):
if zero or not dig in maskDigs:
if dig == "0":
zero = True
else:
return False
return True
9 years ago
def getIpAndMask(interface="eth0"):
"""Get ip and mask from interface"""
9 years ago
ifconfig = process('/sbin/ifconfig', interface)
res = re.search(r"inet addr:(\S+)\s.*Mask:(\S+)", ifconfig.read(), re.S)
if res:
return res.groups()
else:
9 years ago
return "", ""
def strIpToIntIp(addr):
"""Convert ip specified by string to integer"""
addr = addr.split('.')
9 years ago
return ((int(addr[0]) << 24) |
(int(addr[1]) << 16) |
(int(addr[2]) << 8) |
(int(addr[3])))
def intIpToStrIp(addr):
"""Convert ip specified by integer to string"""
return "{0}.{1}.{2}.{3}".format(
9 years ago
addr >> 24, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff)
def numMaskToCidr(netmask):
"""
Convert integer mask to cidr
"""
neg_net = ctypes.c_uint32(~netmask).value
9 years ago
return 32 - int(math.log(neg_net, 2)) - 1 if neg_net else 32
def maskToCidr(mask):
"""Convert mask specified by str to net"""
mask = strIpToIntIp(mask)
return numMaskToCidr(mask)
9 years ago
def cidrToMask(cidr):
"""Convert net to mask specified by str"""
9 years ago
return intIpToStrIp((2 ** cidr - 1) << (32 - cidr))
9 years ago
def getIpNet(ip, mask=None, cidr=None):
"""Get net (xx.xx.xx.xx/xx) by ip address and mask"""
ip = strIpToIntIp(ip)
if not mask is None:
net = maskToCidr(mask)
else:
net = int(cidr)
mask = cidrToMask(net)
mask = strIpToIntIp(mask)
9 years ago
return "{ip}/{net}".format(ip=intIpToStrIp(ip & mask),
net=net)
9 years ago
def isIpInNet(checkip, *ipnets):
"""Check is ip in specified nets"""
9 years ago
return map(lambda x: x[0],
filter(lambda x: strIpToIntIp(checkip) & x[2] == strIpToIntIp(
x[1]) & x[2],
map(lambda x: (
x[0], x[1][0],
strIpToIntIp(cidrToMask(int(x[1][1])))),
map(lambda x: (x, x.partition('/')[0::2]),
ipnets))))
def isDhcpIp(interface="eth0"):
"""Get ip by dhcp or static"""
# dhclients (dhcpcd, dhclient (dhcp), udhcpc (busybox)
commands = getRunCommands()
9 years ago
dhcpProgs = ("dhcpcd", "dhclient", "udhcpc")
if filter(lambda x: interface in x and any(prog in x for prog in dhcpProgs),
commands):
return True
13 years ago
else:
# если запущен демон dhcpcd
9 years ago
if filter(lambda x: "dhcpcd\x00-q" in x, commands):
curIp = getIp(interface)
dhcpcd = getProgPath('/sbin/dhcpcd')
leaseIp = \
9 years ago
map(lambda x: x.group(1),
filter(None,
map(re.compile('^ip_address=(.*)$').search,
process(dhcpcd, '-U', interface))))
if not curIp or leaseIp and leaseIp[0] == curIp:
return True
13 years ago
return False
9 years ago
def getRouteTable(onlyIface=()):
13 years ago
"""Get route table, exclude specifed iface"""
ipProg = checkUtils('/sbin/ip')
9 years ago
routes = process(ipProg, "route")
13 years ago
if onlyIface:
9 years ago
filterRe = re.compile("|".join(map(lambda x: r"dev %s" % x, onlyIface)))
routes = filter(filterRe.search, routes)
13 years ago
for line in routes:
9 years ago
network, op, line = line.partition(" ")
routeParams = map(lambda x: x.strip(), line.split())
13 years ago
# (network,{'via':value,'dev':value})
if network:
9 years ago
yield (network, dict(zip(routeParams[0::2], routeParams[1::2])))
def getInterfaces():
"""
Get available interfaces (discard which hasn't device)
"""
return sorted(
9 years ago
filter(lambda x: path.exists(path.join(SYSFS_NET_PATH, x, "device")),
listDirectory(SYSFS_NET_PATH)))
def getIp(iface):
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
9 years ago
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
except IOError:
return ""
finally:
sockfd.close()
ip = struct.unpack('16sH2x4s8x', res)[2]
return socket.inet_ntoa(ip)
def checkFlag(iface, flag):
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
9 years ago
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFFLAGS, ifreq)
except IOError:
return False
finally:
sockfd.close()
return bool(struct.unpack('16sH2x4s8x', res)[1] & flag)
def getPlugged(iface):
return checkFlag(iface, IFF_RUNNING)
def isSlaveInterface(iface):
return checkFlag(iface, IFF_SLAVE)
def isMasterInterface(iface):
return checkFlag(iface, IFF_MASTER)
9 years ago
def getMask(iface):
"""
Get mask for interface
"""
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
9 years ago
ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFNETMASK, ifreq)
except IOError:
return 0
finally:
sockfd.close()
netmask = socket.ntohl(struct.unpack('16sH2xI8x', res)[2])
return numMaskToCidr(netmask)
9 years ago
def getMac(iface):
"""
Get mac for interface
"""
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
9 years ago
ifreq = struct.pack('16sH14s', iface, socket.AF_UNIX, '\x00' * 14)
try:
res = fcntl.ioctl(sockfd, SIOCGIFHWADDR, ifreq)
except IOError:
return ""
address = struct.unpack('16sH14s', res)[2]
mac = struct.unpack('6B8x', address)
sockfd.close()
return ":".join(['%02X' % i for i in mac])
9 years ago
11 years ago
def getOperState(iface):
"""
Get interface state up or down
"""
9 years ago
if readFile("/sys/class/net/%s/operstate" % iface) == "down":
11 years ago
return "down"
return "up"
9 years ago
def isOpenPort(ip, port):
"""
Test if an [ip:port] is open
"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
9 years ago
s.connect((ip, int(port)))
s.shutdown(2)
return True
9 years ago
except Exception:
return False
9 years ago
class IPError(Exception):
"""Error received on work with ip"""
pass
9 years ago
class Pinger(object):
# ICMP parameters
9 years ago
ICMP_ECHO = 8 # Echo request (per RFC792)
ICMP_MAX_RECV = 2048 # Max size of incoming buffer
9 years ago
def checksum(self, source_string):
"""
A port of the functionality of in_cksum() from ping.c
Ideally this would act on the string as a series of 16-bit ints (host
packed), but this works.
Network data is big-endian, hosts are typically little-endian
"""
9 years ago
countTo = (int(len(source_string) / 2)) * 2
sum = 0
count = 0
# Handle bytes in pairs (decoding as short ints)
while count < countTo:
9 years ago
if sys.byteorder == "little":
loByte = ord(source_string[count])
hiByte = ord(source_string[count + 1])
else:
loByte = ord(source_string[count + 1])
hiByte = ord(source_string[count])
9 years ago
sum += hiByte * 256 + loByte
count += 2
# Handle last byte if applicable (odd-number of bytes)
# Endianness should be irrelevant in this case
9 years ago
if countTo < len(source_string): # Check for odd length
loByte = ord(source_string[len(source_string) - 1])
sum += loByte
9 years ago
# Truncate sum to 32 bits (a variance from ping.c,which
sum &= 0xffffffff
# uses signed ints, but overflow is unlikely in ping)
9 years ago
sum = (sum >> 16) + (sum & 0xffff) # Add high 16 bits to low 16 bits
sum += (sum >> 16) # Add carry from above (if any)
answer = ~sum & 0xffff # Invert and truncate to 16 bits
answer = socket.htons(answer)
return answer
def ping(self, destIP, timeout, numDataBytes):
"""
Returns either the delay (in ms) or None on timeout.
"""
delay = None
9 years ago
try: # One could use UDP here, but it's obscure
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW,
socket.getprotobyname("icmp"))
except socket.error as e:
raise IPError(_("failed. (socket error: '%s')" % e.args[1]))
my_ID = os.getpid() & 0xFFFF
sentTime = self.send_one_ping(mySocket, destIP, my_ID, 0,
9 years ago
numDataBytes)
if sentTime is None:
mySocket.close()
return delay
recvTime, dataSize, iphSrcIP, icmpSeqNumber, iphTTL = \
9 years ago
self.receive_one_ping(mySocket, my_ID, timeout)
mySocket.close()
if recvTime:
9 years ago
delay = (recvTime - sentTime) * 1000
return (dataSize, socket.inet_ntoa(struct.pack("!I", iphSrcIP)),
iphTTL, delay)
else:
raise IPError(_("Request timed out"))
9 years ago
def send_one_ping(self, mySocket, destIP, myID, mySeqNumber, numDataBytes):
"""
Send one ping to the given >destIP<.
"""
try:
9 years ago
destIP = socket.gethostbyname(destIP)
except socket.gaierror as e:
raise IPError(e.strerror)
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
myChecksum = 0
# Make a dummy heder with a 0 checksum.
header = struct.pack(
"!BBHHH", self.ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
padBytes = []
startVal = 0x42
9 years ago
for i in range(startVal, startVal + numDataBytes):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
data = bytes(padBytes)
# Calculate the checksum on the data and the dummy header.
# Checksum is in network order
myChecksum = self.checksum(header + data)
# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
header = struct.pack(
"!BBHHH", self.ICMP_ECHO, 0, myChecksum, myID, mySeqNumber
)
packet = header + data
sendTime = time.time()
try:
# Port number is irrelevant for ICMP
mySocket.sendto(packet, (destIP, 1))
except socket.error as e:
raise IPError("General failure (%s)" % (e.args[1]))
return sendTime
def receive_one_ping(self, mySocket, myID, timeout):
"""
Receive the ping from the socket. Timeout = in ms
"""
9 years ago
timeLeft = timeout / 1000.0
9 years ago
while True: # Loop while waiting for packet or timeout
startedSelect = time.time()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (time.time() - startedSelect)
9 years ago
if isinstance(whatReady[0], list) and not whatReady[0]: # Timeout
return None, 0, 0, 0, 0
timeReceived = time.time()
recPacket, addr = mySocket.recvfrom(self.ICMP_MAX_RECV)
ipHeader = recPacket[:20]
iphVersion, iphTypeOfSvc, iphLength, \
iphID, iphFlags, iphTTL, iphProtocol, \
iphChecksum, iphSrcIP, iphDestIP = struct.unpack(
"!BBHHHBBHII", ipHeader
)
icmpHeader = recPacket[20:28]
icmpType, icmpCode, icmpChecksum, \
icmpPacketID, icmpSeqNumber = struct.unpack(
"!BBHHH", icmpHeader
)
9 years ago
if icmpPacketID == myID: # Our packet
dataSize = len(recPacket) - 28
return timeReceived, dataSize, iphSrcIP, icmpSeqNumber, iphTTL
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None, 0, 0, 0, 0
def check_port(target, port, timeout=5):
"""
Проверить порт (port) на хосте (target)
:param target: хост
:param port: порт
:param timeout: таймаут
:return: True - порт открыт, False - нет хоста, порт закрыт, firewall
"""
try:
targetIP = socket.gethostbyname(target)
except socket.gaierror:
return False
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((targetIP, port))
s.close()
except (socket.error, socket.timeout):
return False
return True