Integer/Round problem in getting the sum of 3 different numbers - python

a = 1541
b = 1575
c = 1512
# I want to ratio the sum of these numbers to 128
total = a + b + c
rounded_a = round(a*128/total) # equals 43
rounded_b = round(b*128/total) # equals 44
rounded_c = round(c*128/total) # equals 42
total_of_rounded = rounded_a + rounded_b + rounded_c # equals 129 NOT 128
# I tried the floor
floor_a = math.floor(a*128/total) # equals 42
floor_b = math.floor(b*128/total) # equals 43
floor_c = math.floor(c*128/total) # equals 41
total_of_floor = floor_a + floor_b + floor_c # equals 126 NOT 128
# The exact values
# a: 42.62057044
# b: 43.56093345
# c: 41,81849611
The question is, how can I reach the total 128?
Note: I should stay at integer, not floating numbers.
Note 2: I can write a correction function which like adding +1 to total but it doesn't seem right to me.

A possibility: round a and b down, then add the missing parts to c.
a = 1541
b = 1575
c = 1512
total = a + b + c # 4628
ra = a * 128 // total
rb = b * 128 // total
rc = (c * 128 + (a * 128)%total + (b*128)%total) // total
print(ra,rb,rc)
# (42, 43, 43)
print(ra+rb+rc)
# 128

This is the way for it:
a = 1541
b = 1575
c = 1512
# I want to ratio the sum of these numbers to 128
total = a + b + c
total_of_round = round((a*128/total)+(b*128/total)+(c*128/total))
print (total_of_round)
Ouput:
128

Related

Minimize AbsEquality rather than enforce in OrTools

I'm trying to solve the following using OR tools:
Given the following bags containing different colors of balls:
bag
red
blue
green
black
A
10
5
85
0
B
25
50
25
0
C
0
100
0
0
D
90
5
5
0
E
2
0
98
0
F
0
0
0
100
How many of each type of bag would I need to have an equal number of each color of ball?
For cases like this where there is an exact answer, the following code:
bags= [
[10,5,85,0],
[25,50,25,0],
[0,100,0,0],
[90,5,5,0],
[2,0,98,0],
[0,0,0,100]
]
bags_n = len(bags)
color_n = len(bags[0])
print(f'Bags: {bags_n}')
print(f'Colors: {color_n}')
color_count= [0] * color_n
for c in range(color_n):
for b in bags:
color_count[c]+= b[c]
print(color_count)
print(f'Inital total: {sum(color_count)}')
print(f'Inital equal share: {sum(color_count)//color_n}')
model = cp_model.CpModel()
weights = []
for r in range(bags_n):
weights.append(model.NewIntVar(1,1000,f'Weight of Bag: {r}'))
total = model.NewIntVar(0, 100000, 'total')
model.Add(
sum(flatten(
[[bags[r][c] * weights[r] for r in range(bags_n)] for c in range(color_n)]
)) == total
)
equal = model.NewIntVar(0, 10000, 'equal share')
model.AddDivisionEquality(equal, total, color_n)
for c in range(color_n):
diff_c = model.NewIntVar(0, 1000, 'diff_'+str(c))
model.Add(diff_c == sum([bags[r][c] * weights[r] for r in range(bags_n)]) - equal)
model.AddAbsEquality(0, diff_c)
solver = cp_model.CpSolver()
status = solver.Solve(model)
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
print(f'Maximum of objective function: {solver.ObjectiveValue()}\n')
for v in weights:
print(f'{solver.Value(v)}')
print(f'total = {solver.Value(total)}')
print(f'equal share = {solver.Value(equal)}')
else:
print(status)
gives back valid weights:
82
2
70
78
5
79
If I change the setup to something like
bags= [
[50,40,10],
[30,20,50],
[30,30,40],
[30,25,45],
]
The model becomes infeasible, I assume due to the fact that there are no weights that satisfy the AbsEquality for every color.
How can I change this to get me the solution closest to an even distribution even if a perfect solution is infeasable?
Christopher Hamkins' suggestion worked great:
bags= [
[50,40,10],
[30,20,50],
[30,30,40],
[30,25,45],
]
bags_n = len(bags)
color_n = len(bags[0])
print(f'Bags: {bags_n}')
print(f'Colors: {color_n}')
color_count= [0] * color_n
for c in range(color_n):
for b in bags:
color_count[c]+= b[c]
print(color_count)
print(["{0:.0%}".format(c/sum(color_count)) for c in color_count])
model = cp_model.CpModel()
weights = []
for r in range(bags_n):
weights.append(model.NewIntVar(1,500,f'Weight of Bag: {r}'))
max = model.NewIntVar(0,100000000,f'Max')
model.AddMaxEquality(max,
[sum([bags[r][c] * weights[r] for r in range(bags_n)]) for c in range(color_n)]
)
min = model.NewIntVar(0,100000000,f'Min')
model.AddMinEquality(min,
[sum([bags[r][c] * weights[r] for r in range(bags_n)]) for c in range(color_n)]
)
diff = model.NewIntVar(0,100000000,f'Diff')
model.Add(max - min == diff)
model.Minimize(diff)
solver = cp_model.CpSolver()
status = solver.Solve(model)
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
print(f'max = {solver.Value(max)}')
print(f'min = {solver.Value(min)}')
print(f'diff = {solver.Value(diff)}')
bag_weights = [0] * bags_n
for i,v in enumerate(weights):
bag_weights[i] = solver.Value(v)
print(f'{solver.Value(v)}')
color_count = [0] * color_n
for c in range(color_n):
for i,b in enumerate(bags):
color_count[c]+= (b[c] * bag_weights[i])
print(color_count)
print(["{0:.0%}".format(c/sum(color_count)) for c in color_count])
else:
print(status)

How to skip combinations to guess string (python)?

I have a 20 byte string, from which I take 5 arrays of four bytes (first four bytes = array 1, etc.).
I have to convert each array into decimals through a specific function.
This way, I'll end up with 5 ints.
I have to add these 5 ints and reach a specific number (4863101420).
Do you have any idea of how to guess one possible combination of the 20 original chars that - going through the process of dividing into arrays and decoding into ints - will add up to 4863101420, without going through something like itertools.combinations_with_replacement?
Since I have 20 possible chars and each is one out of 94 possible chars (printable asciis), it could take a while to compute a string that adds up to 4863101420.
Any insights ?
The function I'm using to convert the char into int is:
def convertCharToDec(charInput):
firstByte = format(ord(charInput[0]), "x")
secondByte = format(ord(charInput[1]), "x")
thirdByte = format(ord(charInput[2]), "x")
fourthByte = format(ord(charInput[3]), "x")
convertedHex = firstByte + secondByte + thirdByte + fourthByte
return int(convertedHex, 16)
Do some maths and you will find out that the final integer is equal to A0×166 + A1×164 + A2×162 + A3, where Ai = sum([ord(the_whole_string[i + j]) for j in range(0, 20, 4)]). It is a sum of 5 bytes, hence 32×5 ≤ Ai ≤ 126×5.
Therefore, you could first try the values of Ai's, then for each of them try the combinations of the 5 bytes. This should greatly reduce the time of computation.
This works only if format(ord(charInput[i]), "x") returns a string of length 2 for any i.
Edit: Here is the math.
Example: 7]f`8^ga:_hb;`id<aje (This is one of the solution)
7]f` -> int(375d6660, 16) = 37*16^6 + 5d*16^4 + 66*16^2 + 60
8^ga -> int(385e6761, 16) = 38*16^6 + 5e*16^4 + 67*16^2 + 61
:_hb -> int(3a5f6862, 16) = 3a*16^6 + 5f*16^4 + 68*16^2 + 62
;`id -> int(3b606964, 16) = 3b*16^6 + 60*16^4 + 69*16^2 + 63
<aje -> int(3c616a65, 16) = 3c*16^6 + 61*16^4 + 6a*16^2 + 64
___________________________________________________________________
A0*16^6 + A1*16^4 + A2*16^2 + A3
Where A0 = 37 + 38 + 3a + 3b + 3c, A1 = 5d + 5e + 5f + 60 + 61 etc.

Convert an amount to Indian Notation in Python

Problem: I need to convert an amount to Indian currency format
My code: I have the following Python implementation:
import decimal
def currencyInIndiaFormat(n):
d = decimal.Decimal(str(n))
if d.as_tuple().exponent < -2:
s = str(n)
else:
s = '{0:.2f}'.format(n)
l = len(s)
i = l-1;
res = ''
flag = 0
k = 0
while i>=0:
if flag==0:
res = res + s[i]
if s[i]=='.':
flag = 1
elif flag==1:
k = k + 1
res = res + s[i]
if k==3 and i-1>=0:
res = res + ','
flag = 2
k = 0
else:
k = k + 1
res = res + s[i]
if k==2 and i-1>=0:
res = res + ','
flag = 2
k = 0
i = i - 1
return res[::-1]
def main():
n = 100.52
print "INR " + currencyInIndiaFormat(n) # INR 100.52
n = 1000.108
print "INR " + currencyInIndiaFormat(n) # INR 1,000.108
n = 1200000
print "INR " + currencyInIndiaFormat(n) # INR 12,00,000.00
main()
My Question: Is there a way to make my currencyInIndiaFormat function shorter, more concise and clean ? / Is there a better way to write my currencyInIndiaFormat function ?
Note: My question is mainly based on Python implementation of the above stated problem. It is not a duplicate of previously asked questions regarding conversion of currency to Indian format.
Indian Currency Format:
For example, numbers here are represented as:
1
10
100
1,000
10,000
1,00,000
10,00,000
1,00,00,000
10,00,00,000
Refer Indian Numbering System
Too much work.
>>> import locale
>>> locale.setlocale(locale.LC_MONETARY, 'en_IN')
'en_IN'
>>> print(locale.currency(100.52, grouping=True))
₹ 100.52
>>> print(locale.currency(1000.108, grouping=True))
₹ 1,000.11
>>> print(locale.currency(1200000, grouping=True))
₹ 12,00,000.00
You can follow these steps.
Install Babel python package from pip
pip install Babel
In your python script
from babel.numbers import format_currency
format_currency(5433422.8012, 'INR', locale='en_IN')
Output:
₹ 54,33,422.80
def formatINR(number):
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
It's simple to use:
print(formatINR(123456))
Output
1,23,456
If you want to handle negative numbers
def negativeFormatINR(number):
negativeNumber = False
if number < 0:
number = abs(number)
negativeNumber = True
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
value = "".join([r] + d)
if negativeNumber:
return '-' + value
return value
It's simple to use:
print(negativeFormatINR(100-10000))
output
-9,900
Note - THIS IS AN ALTERNATIVE SOLUTION FOR ACTUAL QUESTION
If anyone trying to convert in simple Indian terms like K, L, or Cr with 2 floating-point values, the following solution would work.
def format_cash(amount):
def truncate_float(number, places):
return int(number * (10 ** places)) / 10 ** places
if amount < 1e3:
return amount
if 1e3 <= amount < 1e5:
return str(truncate_float((amount / 1e5) * 100, 2)) + " K"
if 1e5 <= amount < 1e7:
return str(truncate_float((amount / 1e7) * 100, 2)) + " L"
if amount > 1e7:
return str(truncate_float(amount / 1e7, 2)) + " Cr"
Examples
format_cash(7843) --> '7.84 K'
format_cash(78436) --> '78.43 K'
format_cash(784367) --> '7.84 L'
format_cash(7843678) --> '78.43 L'
format_cash(78436789) --> '7.84 Cr'
Here is the other way around:
import re
def in_for(value):
value,b=str(value),''
value=''.join(map(lambda va:va if re.match(r'[0-9,.]',va) else '',value))
val=value
if val.count(',')==0:
v,c,a,cc,ii=val,0,[3,2,2],0,0
val=val[:val.rfind('.')] if val.rfind('.')>=0 else val
for i in val[::-1]:
if c==ii and c!=0:
ii+=a[cc%3]
b=','+i+b
cc+=1
else:
b=i+b
c+=1
b=b[1:] if b[0]==',' else b
val=b+v[value.rfind('.'):] if value.rfind('.')>=0 else b
else:
val=str(val).strip('()').replace(' ','')
v=val.rfind('.')
if v>0:
val=val[:v+3]
return val.rstrip('0').rstrip('.') if '.' in val else val
print(in_for('1000000000000.5445'))
Output will be:
10,000,00,00,000.54
(As mentioned in wikipedia indian number system Ex:67,89,000,00,00,000)
def format_indian(t):
dic = {
4:'Thousand',
5:'Lakh',
6:'Lakh',
7:'Crore',
8:'Crore',
9:'Arab'
}
y = 10
len_of_number = len(str(t))
save = t
z=y
while(t!=0):
t=int(t/y)
z*=10
zeros = len(str(z)) - 3
if zeros>3:
if zeros%2!=0:
string = str(save)+": "+str(save/(z/100))[0:4]+" "+dic[zeros]
else:
string = str(save)+": "+str(save/(z/1000))[0:4]+" "+dic[zeros]
return string
return str(save)+": "+str(save)
This code will Convert Yout Numbers to Lakhs, Crores and arabs in most simplest way. Hope it helps.
for i in [1.234567899 * 10**x for x in range(9)]:
print(format_indian(int(i)))
Output:
1: 1
12: 12
123: 123
1234: 1234
12345: 12.3 Thousand
123456: 1.23 Lakh
1234567: 12.3 Lakh
12345678: 1.23 Crore
123456789: 12.3 Crore
Another way:
def formatted_int(value):
# if the value is 100, 10, 1
if len(str(value)) <= 3:
return value
# if the value is 10,000, 1,000
elif 3 < len(str(value)) <= 5:
return f'{str(value)[:-3]},{str(value)[-3:]} ₹'
# if the value is greater the 10,000
else:
cut = str(value)[:-3]
o = []
while cut:
o.append(cut[-2:]) # appending from 1000th value(right to left)
cut = cut[:-2]
o = o[::-1] # reversing list
res = ",".join(o)
return f'{res},{str(value)[-3:]} ₹'
value1 = 1_00_00_00_000
value2 = 10_00_00_00_000
value3 = 100
print(formatted_int(value1))
print(formatted_int(value2))
print(formatted_int(value3))
Ouput:
1,00,00,00,000 ₹
10,00,00,00,000 ₹
100 ₹
As pgksunilkumar's answer, a little improvement is done in case the the number is in between 0 to -1000
def formatINR(number):
if number < 0 and number > -1000:
return number
else:
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
now if the number is between 0 to -1000, the format will not disturb the user.
i.e
a = -600
b = -10000000
c = 700
d = 8000000
print(formatINR(a))
print(formatINR(b))
print(formatINR(c))
print(formatINR(d))
output will be:
-600
-1,00,00,000
700
80,00,000
Couldn't make the other two solutions work for me, so I made something a little more low-tech:
def format_as_indian(input):
input_list = list(str(input))
if len(input_list) <= 1:
formatted_input = input
else:
first_number = input_list.pop(0)
last_number = input_list.pop()
formatted_input = first_number + (
(''.join(l + ',' * (n % 2 == 1) for n, l in enumerate(reversed(input_list)))[::-1] + last_number)
)
if len(input_list) % 2 == 0:
formatted_input.lstrip(',')
return formatted_input
This doesn't work with decimals. If you need that, I would suggest saving the decimal portion into another variable and adding it back in at the end.
num=123456789
snum=str(num)
slen=len(snum)
result=''
if (slen-3)%2 !=0 :
snum='x'+snum
for i in range(0,slen-3,2):
result=result+snum[i:i+2]+','
result+=snum[slen-3:]
print(result.replace('x',''))

While Loop to produce Mathematical Sequences?

I've been asked to do the following:
Using a while loop, you will write a program which will produce the following mathematical sequence:
1 * 9 + 2 = 11(you will compute this number)
12 * 9 + 3 = 111
123 * 9 + 4 = 1111
Then your program should run as far as the results contain only "1"s. You can build your numbers as string, then convert to ints before calculation. Then you can convert the result back to a string to see if it contains all "1"s.
Sample Output:
1 * 9 + 2 = 11
12 * 9 + 3 = 111
123 * 9 + 4 = 1111
1234 * 9 + 5 = 11111
Here is my code:
def main():
Current = 1
Next = 2
Addition = 2
output = funcCalculation(Current, Addition)
while (verifyAllOnes(output) == True):
print(output)
#string concat to get new current number
Current = int(str(Current) + str(Next))
Addition += 1
Next += 1
output = funcCalculation(Current, Next)
def funcCalculation(a,b):
return (a * 9 + b)
def verifyAllOnes(val):
Num_str = str(val)
for ch in Num_str:
if(str(ch)!= "1"):
return False
return True
main()
The bug is that the formula isn't printing next to the series of ones on each line. What am I doing wrong?
Pseudo-code:
a = 1
b = 2
result = a * 9 + b
while string representation of result contains only 1s:
a = concat a with the old value of b, as a number
b = b + 1
result = a * 9 + b
This can be literally converted into Python code.
Testing all ones
Well, for starters, here is one easy way to check that the value is all ones:
def only_ones(n):
n_str = str(n)
return set(n_str) == set(['1'])
You could do something more "mathy", but I'm not sure that it would be any faster. It would much more easily
generalize to other bases (than 10) if that's something you were interested in though
def only_ones(n):
return (n % 10 == 1) and (n == 1 or only_ones2(n / 10))
Uncertainty about how to generate the specific recurrence relation...
As for actually solving the problem though, it's actually not clear what the sequence should be.
What comes next?
123456
1234567
12345678
123456789
?
Is it 1234567890? Or 12345678910? Or 1234567900?
Without answering this, it's not possible to solve the problem in any general way (unless in fact the 111..s
terminate before you get to this issue).
I'm going to go with the most mathematically appealing assumption, which is that the value in question is the
sum of all the 11111... values before it (note that 12 = 11 + 1, 123 = 111 + 11 + 1, 1234 = 1111 + 111 + 11 + 1, etc...).
A solution
In this case, you could do something along these lines:
def sequence_gen():
a = 1
b = 1
i = 2
while only_ones(b):
yield b
b = a*9 + i
a += b
i += 1
Notice that I've put this in a generator in order to make it easier to only grab as many results from this
sequence as you actually want. It's entirely possible that this is an infinite sequence, so actually running
the while code by itself might take a while ;-)
s = sequence_gen()
s.next() #=> 1
s.next() #=> 11
A generator gives you a lot of flexibility for things like this. For instance, you could grab the first 10 values of the sequence using the itertools.islice
function:
import itertools as it
s = sequence_gen()
xs = [x for x in it.islice(s, 10)]
print xs

Malformed Packet on ICMP python

How i can create a frame icmp
I mark malformet packet on python
import socket, struct, uuid, subprocess, fcntl, time,random
List item
from binascii import hexlify, unhexlify from datetime import datetime
tarRed = raw_input('Ingrese nombre de Tarjeta de Red Utilizada: ')
subprocess.call(['ifconfig', tarRed, 'promisc']) pt = 0x0800 s =
socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(pt))
s.bind((tarRed, pt))
#GETCheck def getchecksum(ip_header,size): cksum = 0 pointer = 0 while size > 1: cksum += int((str("%02x" % (ip_header[pointer],))
+ str("%02x" % (ip_header[pointer+1],))), 16) size -= 2 pointer += 2 #if size: #This accounts for a situation where the header is odd
#cksum += int(ip_header[pointer]) cksum = (cksum >> 16) +
(cksum & 0xffff) cksum += (cksum >>16) return (~cksum) &
0xFFFF def _checksum(data): #calculate the header sum
ip_header_sum = sum(struct.unpack_from("6H", data)) #add the carry
ip_header_sum = (ip_header_sum & 0xFFFF) + (ip_header_sum >> 8 &
0xFFFF) ip_header_sum = ~ip_header_sum & 0xFFFF return
ip_header_sum def i_checksum(checksum_packet): total = 0 num_words
= len(checksum_packet) / 2 for chunk in struct.unpack("!%sH" % num_words, checksum_packet[0:num_words*2]): total += chunk if
len(checksum_packet) % 2: total += ord(checksum_packet[-1]) << 8
total += total >> 16 return (~total + 0xffff & 0xffff)
def checksum(source_string): sum = 0 countTo =
(len(source_string)/2)*2 count = 0 while count<countTo:
thisVal = ord(source_string[count + 1])*256 + ord(source_string[count]) sum = sum + thisVal sum = sum &
0xffffffff
count = count + 2 if countTo<len(source_string): sum = sum + ord(source_string[len(source_string) - 1]) sum = sum &
0xffffffff sum = (sum >> 16) + (sum & 0xffff) sum = sum + (sum
>> 16) answer = ~sum answer = answer & 0xffff #answer = answer >> 8 | (answer << 8 & 0xff00) return answer def
checksuma(source_string): sum = 0 countTo =
(len(source_string)/2)*2 count = 0 while count<countTo:
thisVal = ord(source_string[count + 1])*256 + ord(source_string[count]) sum = sum + thisVal sum = sum &
0xffffffff
count = count + 2 if countTo<len(source_string): sum = sum + ord(source_string[len(source_string) - 1]) sum = sum &
0xffffffff sum = (sum >> 16) + (sum & 0xffff) sum = sum + (sum
>> 16) answer = ~sum answer = answer & 0xffff answer = answer >> 8 | (answer << 8 & 0xff00) return answer def ultimo(str):
csum = 0
countTo = (len(str) / 2) * 2
count = 0
while count < countTo:
thisVal = ord(str[count+1]) * 256 + ord(str[count])
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(str):
csum = csum + ord(str[len(str) - 1])
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
return answer
#GETCheck
#MACs adst='ffffffffffff' mac_destino = unhexlify(adst) ma = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) info =
fcntl.ioctl(ma.fileno(), 0x8927, struct.pack('256s', tarRed[:15]))
asrc = ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
sd = asrc.split(':') asrc = sd[0]+sd[1]+sd[2]+sd[3]+sd[4]+sd[5]
mac_origen = unhexlify(asrc)
#MACs cabEther = struct.pack('!6s6sh',mac_destino,mac_origen,pt)
#datosIP
#version = '4'
#IHL = '5' tipoServicio = unhexlify('00') longitudT = struct.pack('!BB',00,24) identificador = struct.pack('!BB',00,01)
flag_Pos = 0x4000 tiempoVida = 05 ptIP = unhexlify('01') SCC = 0
#IPs ip = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ipO= str( socket.inet_ntoa(fcntl.ioctl(ip.fileno(),0x8915,struct.pack('256s',
tarRed[:15]))[20:24])) ipO1 = ipO.split('.') ipOr =
struct.pack("!BBBB",int(ipO1[0]),int(ipO1[1]),int(ipO1[2]),int(ipO1[3]))
ipD = raw_input('Ingrese la direccion IP destino: ') ipD1 =
ipD.split('.') ipDest =
struct.pack("!BBBB",int(ipD1[0]),int(ipD1[1]),int(ipD1[2]),int(ipD1[3]))
#IPs
#datosIP cabIP = struct.pack('!1s1s2s2shB1sH4s4s',unhexlify('45'),tipoServicio,longitudT,identificador,flag_Pos,tiempoVida,ptIP,SCC,ipOr,ipDest)
SCC = checksum(cabIP) cabIP =
struct.pack('!1s1s2s2shB1sH4s4s',unhexlify('45'),tipoServicio,longitudT,identificador,flag_Pos,tiempoVida,ptIP,socket.htons(SCC),ipOr,ipDest)
#datosIcmp tipo = 8 codigo = 0x00 check = 0x0000 identificador = int((id(1) * random.random()) % 65535) secuencia = 0x0000
#datos = 'qwertyasdfghzxcvbn0102030405060708091011121314151617181920' datos =
1 * 'Q'
#datosIcmp cabIcmp = struct.pack('!bbHHh',tipo,0,0,identificador,1) my_checksum = ultimo(cabIcmp + datos) cabIcmp =
struct.pack('!bbHHh',tipo,0,socket.htons(my_checksum),identificador,1)
cabIcmp = cabIcmp + datos tiempo = datetime.now() print tiempo for i
in range(15): s.send(cabEther + cabIP + cabIcmp,0)
Your question is barely understandable. It appears you're asking how to create an ICMP ECHO packet since the packet you are creating in the listed code is reported as malformed by your system. I'll try to answer why your code won't work.
First, the socket type you're using is incorrect. You need type 1, not 8. This can be done easily enough with the third parameter:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
(Note you'll need root or administrator privileges to make this call.)
Second, after creating the socket, you'll need to create the ICMP header. An ICMP header consists of the following structure:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 | Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4 | REST OF HEADER |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Notice the second four bytes "REST OF HEADER". This second set of four bytes depends on the TYPE/CODE in the first two bytes. For example, if TYPE is 0 and CODE is 0, it means you're dealing with an echo reply, and as such, the second four bytes will become 2-byte Identifier and 2-byte Sequence Number fields, starting at offset 4 and ending at offset 7 as the picture shows:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0 | Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4 | Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Data ...
+-+-+-+-+-
To create this header, I'm going to use type 8, the echo request structure, as such (note that bytes 4-5 and 6-7 in this creation is just for illustration, typically this would be different values other than zero):
icmp = struct.pack(">BBHHH", 8, 0, 0, 0, 0)
On this initial creation of the header, the checksum is zero. What we now need is to actually calculate the checksum (with this zero value), and THEN recreate the header with this new checksum value:
icmp = struct.pack(">BBHHH", 8, 0, cksum(icmp), 0, 0)
But this is where your code is incorrect again. The implementation of i_checksum() is wrong. To fix, I'll leave it to you to figure it out. The checksum is called 1's complement, and there are numerous online articles about it, as well as C code on Linux systems that implement it.
Once you have the header and its correct checksum (important because your system won't send the packet if incorrect), you simply then send it somewhere:
s.sendto(icmp, (SOME_REMOTE_HOST, 0))
And then receive replies if any:
s.recvfrom(1500)
(Note I chose 1500 bytes as that is the MTU of an IP frame, and it is thus unlikely a reply will be bigger than this, though it is certainly possible.)
AND AT THIS POINT, YOUR CODE TOTALLY FAILS -- simply because you do not have code which does the last two steps, let alone additional code to handle what to do with replies and struct-parsing of the header fields.
As for the other two functions you've defined, they're odd. One of them seems to imply it is dealing with link layer fields -- which is never the case on sockets as sockets only deal in IP layer and above -- while the other seems to be an attempt at calculating a checksum...albeit incorrectly (again).
In short, as abarnert implied, please clarify (and add some more) code AND your question.

Categories