1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2022 Huawei Device Co., Ltd.
4 *
5 * Description: Provides some functionalities for
6 * checksum calculation in the NewIP protocol.
7 *
8 * Author: Yang Yanjun <yangyanjun@huawei.com>
9 *
10 * Data: 2022-07-18
11 */
12#include "nip_hdr.h"
13#include "nip_checksum.h"
14
15#define USHORT_PAYLOAD 16
16#define NIP_CHECKSUM_UINT8_PAYLOAD 8
17unsigned int _nip_check_sum(const unsigned char *data, unsigned short data_len)
18{
19	unsigned int i = 0;
20	unsigned int sum = 0;
21
22	while (i + 1 < data_len) {
23		sum += (data[i] << NIP_CHECKSUM_UINT8_PAYLOAD) + data[i + 1];
24		i += 2; /* Offset 2 bytes */
25	}
26
27	if (i < (unsigned int)data_len)
28		sum += (data[i] << NIP_CHECKSUM_UINT8_PAYLOAD);
29
30	return sum;
31}
32
33unsigned int _nip_header_chksum(struct nip_pseudo_header *chksum_header)
34{
35	int i, j;
36	int addr_len;
37	unsigned char pseudo_header[NIP_HDR_MAX] = {0};
38	unsigned short hdr_len = 0;
39
40	addr_len = chksum_header->saddr.bitlen / NIP_ADDR_BIT_LEN_8;
41	if (addr_len && addr_len < NIP_HDR_MAX) {
42		j = 0;
43		for (i = 0; i < addr_len; i++, j++)
44			pseudo_header[j] = chksum_header->saddr.NIP_ADDR_FIELD8[i];
45		hdr_len += addr_len;
46	}
47
48	addr_len = chksum_header->daddr.bitlen / NIP_ADDR_BIT_LEN_8;
49	if (addr_len && addr_len < NIP_HDR_MAX) {
50		j = hdr_len;
51		for (i = 0; i < addr_len; i++, j++)
52			pseudo_header[j] = chksum_header->daddr.NIP_ADDR_FIELD8[i];
53		hdr_len += addr_len;
54	}
55
56	/* chksum_header->check_len is network order.(big end) */
57	if (hdr_len < NIP_HDR_MAX) {
58		*(unsigned short *)(pseudo_header + hdr_len) = chksum_header->check_len;
59		hdr_len += sizeof(chksum_header->check_len);
60	}
61
62	if (hdr_len < NIP_HDR_MAX) {
63		*(pseudo_header + hdr_len) = chksum_header->nexthdr;
64		hdr_len += sizeof(chksum_header->nexthdr);
65	}
66
67	return _nip_check_sum(pseudo_header, hdr_len);
68}
69
70/* The checksum is calculated when the packet is received
71 * Note:
72 * 1.chksum_header->check_len is network order.(big end)
73 * 2.check_len is host order.
74 */
75unsigned short nip_check_sum_parse(unsigned char *data,
76				   unsigned short check_len,
77				   struct nip_pseudo_header *chksum_header)
78{
79	unsigned int sum = 0;
80
81	sum = _nip_check_sum(data, check_len);
82	sum += _nip_header_chksum(chksum_header);
83
84	while (sum >> USHORT_PAYLOAD)
85		sum = (sum >> USHORT_PAYLOAD) + (sum & 0xffff);
86	return (unsigned short)sum;
87}
88
89/* The checksum is calculated when the packet is sent
90 * Note:
91 * 1.chksum_header->check_len is network order.(big end)
92 * 2.data_len is host order.
93 */
94unsigned short nip_check_sum_build(unsigned char *data,
95				   unsigned short data_len,
96				   struct nip_pseudo_header *chksum_header)
97{
98	unsigned int sum = 0;
99
100	sum = _nip_check_sum(data, data_len);
101	sum += _nip_header_chksum(chksum_header);
102
103	while (sum >> USHORT_PAYLOAD)
104		sum = (sum >> USHORT_PAYLOAD) + (sum & 0xffff);
105	return (unsigned short)(~sum);
106}
107
108