mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-22 04:09:23 +08:00
ICMP实现 ip数据报分片机制还未实现
This commit is contained in:
BIN
cmd/arp/arp
BIN
cmd/arp/arp
Binary file not shown.
@@ -2,6 +2,7 @@ package header
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"netstack/tcpip"
|
"netstack/tcpip"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -106,6 +107,12 @@ const (
|
|||||||
IPv4Any tcpip.Address = "\x00\x00\x00\x00"
|
IPv4Any tcpip.Address = "\x00\x00\x00\x00"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Flags that may be set in an IPv4 packet.
|
||||||
|
const (
|
||||||
|
IPv4FlagMoreFragments = 1 << iota
|
||||||
|
IPv4FlagDontFragment
|
||||||
|
)
|
||||||
|
|
||||||
func IPVersion(b []byte) int {
|
func IPVersion(b []byte) int {
|
||||||
if len(b) < versIHL+1 {
|
if len(b) < versIHL+1 {
|
||||||
return -1
|
return -1
|
||||||
@@ -216,6 +223,11 @@ func (b IPv4) SetDestinationAddress(addr tcpip.Address) {
|
|||||||
copy(b[dstAddr:dstAddr+IPv4AddressSize], addr)
|
copy(b[dstAddr:dstAddr+IPv4AddressSize], addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateChecksum calculates the checksum of the ipv4 header.
|
||||||
|
func (b IPv4) CalculateChecksum() uint16 {
|
||||||
|
return Checksum(b[:b.HeaderLength()], 0)
|
||||||
|
}
|
||||||
|
|
||||||
// Encode encodes all the fields of the ipv4 header.
|
// Encode encodes all the fields of the ipv4 header.
|
||||||
func (b IPv4) Encode(i *IPv4Fields) {
|
func (b IPv4) Encode(i *IPv4Fields) {
|
||||||
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
|
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
|
||||||
@@ -264,3 +276,28 @@ func IsV4MulticastAddress(addr tcpip.Address) bool {
|
|||||||
}
|
}
|
||||||
return (addr[0] & 0xf0) == 0xe0
|
return (addr[0] & 0xf0) == 0xe0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipv4Fmt string = `
|
||||||
|
|% 4s|% 4s|% 8s| % 16s|
|
||||||
|
| % 16s|%s|%s|%s|% 11s|
|
||||||
|
| % 8s|% 8s|% 16s |
|
||||||
|
|% 32s |
|
||||||
|
|% 32s |
|
||||||
|
| Options | Padding |
|
||||||
|
%v
|
||||||
|
`
|
||||||
|
|
||||||
|
type Types [] struct {}
|
||||||
|
|
||||||
|
func atoi[T int | int8 | int16 | int32 | int64 | uint | uint8 |uint16 | uint32](i T) string {
|
||||||
|
return fmt.Sprintf("%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b IPv4) String() string {
|
||||||
|
return fmt.Sprintf(ipv4Fmt, atoi(IPVersion(b)), atoi(b.HeaderLength()), atoi(0), atoi(b.TotalLength()),
|
||||||
|
atoi(b.ID()), atoi(b.Flags()>>2), atoi((b.Flags()&2)>>1), atoi(b.Flags()&1), atoi(b.FragmentOffset()),
|
||||||
|
atoi(b.TTL()), atoi(b.Protocol()), atoi(b.Checksum()),
|
||||||
|
b.SourceAddress().String(),
|
||||||
|
b.DestinationAddress().String(),
|
||||||
|
b.Payload())
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package fdbased
|
package fdbased
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"netstack/tcpip"
|
"netstack/tcpip"
|
||||||
"netstack/tcpip/buffer"
|
"netstack/tcpip/buffer"
|
||||||
"netstack/tcpip/header"
|
"netstack/tcpip/header"
|
||||||
@@ -126,6 +127,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
|
|||||||
ethHdr.SrcAddr = e.addr // 说明这是一个原始报文
|
ethHdr.SrcAddr = e.addr // 说明这是一个原始报文
|
||||||
}
|
}
|
||||||
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
|
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
|
||||||
|
log.Println("链路层写回报文")
|
||||||
// 写入网卡中
|
// 写入网卡中
|
||||||
if payload.Size() == 0 {
|
if payload.Size() == 0 {
|
||||||
return rawfile.NonBlockingWrite(e.fd, hdr.View())
|
return rawfile.NonBlockingWrite(e.fd, hdr.View())
|
||||||
|
128
tcpip/network/ipv4/icmp.go
Normal file
128
tcpip/network/ipv4/icmp.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package ipv4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"log"
|
||||||
|
"netstack/tcpip"
|
||||||
|
"netstack/tcpip/buffer"
|
||||||
|
"netstack/tcpip/header"
|
||||||
|
"netstack/tcpip/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ICMP 的全称是 Internet Control Message Protocol 。与 IP 协议一样同属 TCP/IP 模型中的网络层,并且 ICMP 数据包是包裹在 IP 数据包中的
|
||||||
|
|
||||||
|
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
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Type | Code | Checksum |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| |
|
||||||
|
| 不同的Type和Code有不同的内容 |
|
||||||
|
| |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
*/
|
||||||
|
|
||||||
|
type echoRequest struct {
|
||||||
|
r stack.Route
|
||||||
|
v buffer.View
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleControl处理ICMP数据包包含导致ICMP发送的原始数据包的标头的情况。
|
||||||
|
// 此信息用于确定必须通知哪个传输端点有关ICMP数据包。
|
||||||
|
func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.VectorisedView) {
|
||||||
|
h := header.IPv4(vv.First())
|
||||||
|
|
||||||
|
// We don't use IsValid() here because ICMP only requires that the IP
|
||||||
|
// header plus 8 bytes of the transport header be included. So it's
|
||||||
|
// likely that it is truncated, which would cause IsValid to return
|
||||||
|
// false.
|
||||||
|
//
|
||||||
|
// Drop packet if it doesn't have the basic IPv4 header or if the
|
||||||
|
// original source address doesn't match the endpoint's address.
|
||||||
|
if len(h) < header.IPv4MinimumSize || h.SourceAddress() != e.id.LocalAddress {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen := int(h.HeaderLength())
|
||||||
|
if vv.Size() < hlen || h.FragmentOffset() != 0 {
|
||||||
|
// We won't be able to handle this if it doesn't contain the
|
||||||
|
// full IPv4 header, or if it's a fragment not at offset 0
|
||||||
|
// (because it won't have the transport header).
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the ip header, then deliver control message.
|
||||||
|
vv.TrimFront(hlen)
|
||||||
|
p := h.TransportProtocol()
|
||||||
|
e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, vv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理ICMP报文
|
||||||
|
func (e *endpoint) handleICMP(r *stack.Route, vv buffer.VectorisedView) {
|
||||||
|
v := vv.First()
|
||||||
|
if len(v) < header.ICMPv4MinimumSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h := header.ICMPv4(v)
|
||||||
|
|
||||||
|
// 更具icmp的类型来进行相应的处理
|
||||||
|
switch h.Type() {
|
||||||
|
case header.ICMPv4Echo: // icmp echo请求
|
||||||
|
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("ICMP echo")
|
||||||
|
vv.TrimFront(header.ICMPv4MinimumSize) // 去掉头部
|
||||||
|
req := echoRequest{r: r.Clone(), v: vv.ToView()}
|
||||||
|
select {
|
||||||
|
case e.echoRequests <- req: // 发送给echoReplier处理 在那里会重新组一个头部
|
||||||
|
default:
|
||||||
|
req.r.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
case header.ICMPv4EchoReply: // icmp echo响应
|
||||||
|
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, vv)
|
||||||
|
|
||||||
|
case header.ICMPv4DstUnreachable: // 目标不可达
|
||||||
|
if len(v) < header.ICMPv4DstUnreachableMinimumSize {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vv.TrimFront(header.ICMPv4DstUnreachableMinimumSize)
|
||||||
|
switch h.Code() {
|
||||||
|
case header.ICMPv4PortUnreachable: // 端口不可达
|
||||||
|
e.handleControl(stack.ControlPortUnreachable, 0, vv)
|
||||||
|
|
||||||
|
case header.ICMPv4FragmentationNeeded: // 需要进行分片但设置不分片标志
|
||||||
|
mtu := uint32(binary.BigEndian.Uint16(v[header.ICMPv4DstUnreachableMinimumSize-2:]))
|
||||||
|
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理icmp echo请求的goroutine
|
||||||
|
func (e *endpoint) echoReplier() {
|
||||||
|
for req := range e.echoRequests {
|
||||||
|
sendPing4(&req.r, 0, req.v)
|
||||||
|
req.r.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据icmp echo请求,封装icmp echo响应报文,并传给ip层处理
|
||||||
|
func sendPing4(r *stack.Route, code byte, data buffer.View) *tcpip.Error {
|
||||||
|
hdr := buffer.NewPrependable(header.ICMPv4EchoMinimumSize + int(r.MaxHeaderLength()))
|
||||||
|
|
||||||
|
icmpv4 := header.ICMPv4(hdr.Prepend(header.ICMPv4EchoMinimumSize))
|
||||||
|
icmpv4.SetType(header.ICMPv4EchoReply)
|
||||||
|
icmpv4.SetCode(code)
|
||||||
|
copy(icmpv4[header.ICMPv4MinimumSize:], data)
|
||||||
|
data = data[header.ICMPv4EchoMinimumSize-header.ICMPv4MinimumSize:]
|
||||||
|
icmpv4.SetChecksum(^header.Checksum(icmpv4, header.Checksum(data, 0)))
|
||||||
|
|
||||||
|
log.Printf("ICMP 回应报文组完 再次包装到IP报文")
|
||||||
|
// 传给ip层处理
|
||||||
|
return r.WritePacket(hdr, data.ToVectorisedView(), header.ICMPv4ProtocolNumber, r.DefaultTTL())
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"netstack/tcpip"
|
"netstack/tcpip"
|
||||||
"netstack/tcpip/buffer"
|
"netstack/tcpip/buffer"
|
||||||
"netstack/tcpip/header"
|
"netstack/tcpip/header"
|
||||||
@@ -30,7 +31,12 @@ type endpoint struct {
|
|||||||
id stack.NetworkEndpointID
|
id stack.NetworkEndpointID
|
||||||
// 链路端的表示
|
// 链路端的表示
|
||||||
linkEP stack.LinkEndpoint
|
linkEP stack.LinkEndpoint
|
||||||
// TODO 需要添加
|
// 报文分发器
|
||||||
|
dispatcher stack.TransportDispatcher
|
||||||
|
// ping请求报文接收队列
|
||||||
|
echoRequests chan echoRequest
|
||||||
|
// ip报文分片处理器
|
||||||
|
//fragmentation *fragmentation.Fragmentation
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultTTL is the default time-to-live value for this endpoint.
|
// DefaultTTL is the default time-to-live value for this endpoint.
|
||||||
@@ -73,13 +79,82 @@ func (e *endpoint) MaxHeaderLength() uint16 {
|
|||||||
// 将传输层的数据封装加上IP头,并调用网卡的写入接口,写入IP报文
|
// 将传输层的数据封装加上IP头,并调用网卡的写入接口,写入IP报文
|
||||||
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView,
|
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView,
|
||||||
protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
|
protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
|
||||||
return nil
|
// 预留ip报文的空间
|
||||||
|
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
|
||||||
|
length := uint16(hdr.UsedLength() + payload.Size())
|
||||||
|
id := uint32(0)
|
||||||
|
// 如果报文长度大于68
|
||||||
|
if length > header.IPv4MaximumHeaderSize+8 {
|
||||||
|
// Packets of 68 bytes or less are required by RFC 791 to not be
|
||||||
|
// fragmented, so we only assign ids to larger packets.
|
||||||
|
//id = atomic.AddUint32(&ids[hashRoute(r, protocol)%buckets], 1)
|
||||||
|
}
|
||||||
|
// ip首部编码
|
||||||
|
ip.Encode(&header.IPv4Fields{
|
||||||
|
IHL: header.IPv4MinimumSize,
|
||||||
|
TotalLength: length,
|
||||||
|
ID: uint16(id),
|
||||||
|
TTL: ttl,
|
||||||
|
Protocol: uint8(protocol),
|
||||||
|
SrcAddr: r.LocalAddress,
|
||||||
|
DstAddr: r.RemoteAddress,
|
||||||
|
})
|
||||||
|
// 计算校验和和设置校验和
|
||||||
|
ip.SetChecksum(^ip.CalculateChecksum())
|
||||||
|
r.Stats().IP.PacketsSent.Increment()
|
||||||
|
|
||||||
|
// 写入网卡接口
|
||||||
|
if protocol == header.ICMPv4ProtocolNumber {
|
||||||
|
log.Printf("IP 写回ICMP报文 长度: %d\n", hdr.UsedLength()+payload.Size())
|
||||||
|
} else {
|
||||||
|
log.Printf("send ipv4 packet %d bytes, proto: 0x%x", hdr.UsedLength()+payload.Size(), protocol)
|
||||||
|
}
|
||||||
|
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlePacket is called by the link layer when new ipv4 packets arrive for
|
// HandlePacket is called by the link layer when new ipv4 packets arrive for
|
||||||
// this endpoint.
|
// this endpoint.
|
||||||
// 收到ip包的处理
|
// 收到ip包的处理
|
||||||
func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
|
func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
|
||||||
|
// 得到ip报文
|
||||||
|
h := header.IPv4(vv.First())
|
||||||
|
// 检查报文是否有效
|
||||||
|
if !h.IsValid(vv.Size()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println(h)
|
||||||
|
|
||||||
|
hlen := int(h.HeaderLength())
|
||||||
|
tlen := int(h.TotalLength())
|
||||||
|
vv.TrimFront(hlen)
|
||||||
|
vv.CapLength(tlen - hlen)
|
||||||
|
|
||||||
|
// 报文重组 TODO
|
||||||
|
more := (h.Flags() & header.IPv4FlagMoreFragments) != 0
|
||||||
|
// 是否需要ip重组
|
||||||
|
if more || h.FragmentOffset() != 0 {
|
||||||
|
// The packet is a fragment, let's try to reassemble it.
|
||||||
|
last := h.FragmentOffset() + uint16(vv.Size()) - 1
|
||||||
|
var ready bool
|
||||||
|
log.Println(last)
|
||||||
|
// ip分片重组
|
||||||
|
//vv, ready = e.fragmentation.Process(hash.IPv4FragmentHash(h), h.FragmentOffset(), last, more, vv)
|
||||||
|
if !ready {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 得到传输层的协议
|
||||||
|
p := h.TransportProtocol()
|
||||||
|
// 如果时ICMP协议,则进入ICMP处理函数
|
||||||
|
if p == header.ICMPv4ProtocolNumber {
|
||||||
|
e.handleICMP(r, vv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Stats().IP.PacketsDelivered.Increment()
|
||||||
|
// 根据协议分发到不同处理函数,比如协议时TCP,会进入tcp.HandlePacket
|
||||||
|
log.Printf("recv ipv4 packet %d bytes, proto: 0x%x", tlen, p)
|
||||||
|
e.dispatcher.DeliverTransportPacket(r, p, vv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close cleans up resources associated with the endpoint.
|
// Close cleans up resources associated with the endpoint.
|
||||||
@@ -94,11 +169,15 @@ type protocol struct{}
|
|||||||
func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCache stack.LinkAddressCache,
|
func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCache stack.LinkAddressCache,
|
||||||
dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) (stack.NetworkEndpoint, *tcpip.Error) {
|
dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) (stack.NetworkEndpoint, *tcpip.Error) {
|
||||||
e := &endpoint{
|
e := &endpoint{
|
||||||
nicid: nicid,
|
nicid: nicid,
|
||||||
id: stack.NetworkEndpointID{LocalAddress: addr},
|
id: stack.NetworkEndpointID{LocalAddress: addr},
|
||||||
linkEP: linkEP,
|
linkEP: linkEP,
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
echoRequests: make(chan echoRequest, 10),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go e.echoReplier()
|
||||||
|
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,9 +201,8 @@ func (p *protocol) MinimumPacketSize() int {
|
|||||||
|
|
||||||
// ParseAddresses implements NetworkProtocol.ParseAddresses.
|
// ParseAddresses implements NetworkProtocol.ParseAddresses.
|
||||||
func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
|
func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
|
||||||
//h := header.IPv4(v)
|
h := header.IPv4(v)
|
||||||
//return h.SourceAddress(), h.DestinationAddress()
|
return h.SourceAddress(), h.DestinationAddress()
|
||||||
return "", ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption implements NetworkProtocol.SetOption.
|
// SetOption implements NetworkProtocol.SetOption.
|
||||||
|
7
tcpip/network/ipv4/ipv4_test.go
Normal file
7
tcpip/network/ipv4/ipv4_test.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package ipv4_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIPv4Base(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
@@ -238,10 +238,10 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address,
|
|||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
for i := range s.routeTable {
|
for i := range s.routeTable {
|
||||||
//if (id != 0 && id != s.routeTable[i].NIC) ||
|
if (id != 0 && id != s.routeTable[i].NIC) ||
|
||||||
// (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
|
(len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
|
||||||
// continue
|
continue
|
||||||
//}
|
}
|
||||||
|
|
||||||
nic := s.nics[s.routeTable[i].NIC]
|
nic := s.nics[s.routeTable[i].NIC]
|
||||||
if nic == nil {
|
if nic == nil {
|
||||||
|
@@ -183,6 +183,21 @@ type Route struct {
|
|||||||
NIC NICID // 使用的网卡设备
|
NIC NICID // 使用的网卡设备
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match determines if r is viable for the given destination address.
|
||||||
|
func (r *Route) Match(addr Address) bool {
|
||||||
|
if len(addr) != len(r.Destination) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(r.Destination); i++ {
|
||||||
|
if (addr[i] & r.Mask[i]) != r.Destination[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Stats 包含了网络栈的统计信息
|
// Stats 包含了网络栈的统计信息
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
// TODO 需要解读
|
// TODO 需要解读
|
||||||
|
Reference in New Issue
Block a user