1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.ip; 18 19 import static android.net.netlink.ConntrackMessage.DYING_MASK; 20 import static android.net.netlink.ConntrackMessage.ESTABLISHED_MASK; 21 22 import android.net.netlink.ConntrackMessage; 23 import android.net.netlink.NetlinkConstants; 24 import android.net.netlink.NetlinkMessage; 25 import android.net.util.SharedLog; 26 import android.os.Handler; 27 import android.system.OsConstants; 28 29 import androidx.annotation.NonNull; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 33 import java.util.Objects; 34 35 36 /** 37 * ConntrackMonitor. 38 * 39 * Monitors the netfilter conntrack notifications and presents to callers 40 * ConntrackEvents describing each event. 41 * 42 * @hide 43 */ 44 public class ConntrackMonitor extends NetlinkMonitor { 45 private static final String TAG = ConntrackMonitor.class.getSimpleName(); 46 private static final boolean DBG = false; 47 private static final boolean VDBG = false; 48 49 // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h 50 public static final int NF_NETLINK_CONNTRACK_NEW = 1; 51 public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; 52 public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; 53 54 /** 55 * A class for describing parsed netfilter conntrack events. 56 */ 57 public static class ConntrackEvent { 58 /** 59 * Conntrack event type. 60 */ 61 public final short msgType; 62 /** 63 * Original direction conntrack tuple. 64 */ 65 public final ConntrackMessage.Tuple tupleOrig; 66 /** 67 * Reply direction conntrack tuple. 68 */ 69 public final ConntrackMessage.Tuple tupleReply; 70 /** 71 * Connection status. A bitmask of ip_conntrack_status enum flags. 72 */ 73 public final int status; 74 /** 75 * Conntrack timeout. 76 */ 77 public final int timeoutSec; 78 ConntrackEvent(ConntrackMessage msg)79 public ConntrackEvent(ConntrackMessage msg) { 80 this.msgType = msg.getHeader().nlmsg_type; 81 this.tupleOrig = msg.tupleOrig; 82 this.tupleReply = msg.tupleReply; 83 this.status = msg.status; 84 this.timeoutSec = msg.timeoutSec; 85 } 86 87 @VisibleForTesting ConntrackEvent(short msgType, ConntrackMessage.Tuple tupleOrig, ConntrackMessage.Tuple tupleReply, int status, int timeoutSec)88 public ConntrackEvent(short msgType, ConntrackMessage.Tuple tupleOrig, 89 ConntrackMessage.Tuple tupleReply, int status, int timeoutSec) { 90 this.msgType = msgType; 91 this.tupleOrig = tupleOrig; 92 this.tupleReply = tupleReply; 93 this.status = status; 94 this.timeoutSec = timeoutSec; 95 } 96 97 @Override 98 @VisibleForTesting equals(Object o)99 public boolean equals(Object o) { 100 if (!(o instanceof ConntrackEvent)) return false; 101 ConntrackEvent that = (ConntrackEvent) o; 102 return this.msgType == that.msgType 103 && Objects.equals(this.tupleOrig, that.tupleOrig) 104 && Objects.equals(this.tupleReply, that.tupleReply) 105 && this.status == that.status 106 && this.timeoutSec == that.timeoutSec; 107 } 108 109 @Override hashCode()110 public int hashCode() { 111 return Objects.hash(msgType, tupleOrig, tupleReply, status, timeoutSec); 112 } 113 114 @Override toString()115 public String toString() { 116 return "ConntrackEvent{" 117 + "msg_type{" 118 + NetlinkConstants.stringForNlMsgType(msgType, OsConstants.NETLINK_NETFILTER) 119 + "}, " 120 + "tuple_orig{" + tupleOrig + "}, " 121 + "tuple_reply{" + tupleReply + "}, " 122 + "status{" 123 + status + "(" + ConntrackMessage.stringForIpConntrackStatus(status) + ")" 124 + "}, " 125 + "timeout_sec{" + Integer.toUnsignedLong(timeoutSec) + "}" 126 + "}"; 127 } 128 129 /** 130 * Check the established NAT session conntrack message. 131 * 132 * @param msg the conntrack message to check. 133 * @return true if an established NAT message, false if not. 134 */ isEstablishedNatSession(@onNull ConntrackMessage msg)135 public static boolean isEstablishedNatSession(@NonNull ConntrackMessage msg) { 136 if (msg.getMessageType() != NetlinkConstants.IPCTNL_MSG_CT_NEW) return false; 137 if (msg.tupleOrig == null) return false; 138 if (msg.tupleReply == null) return false; 139 if (msg.timeoutSec == 0) return false; 140 if ((msg.status & ESTABLISHED_MASK) != ESTABLISHED_MASK) return false; 141 142 return true; 143 } 144 145 /** 146 * Check the dying NAT session conntrack message. 147 * Note that IPCTNL_MSG_CT_DELETE event has no CTA_TIMEOUT attribute. 148 * 149 * @param msg the conntrack message to check. 150 * @return true if a dying NAT message, false if not. 151 */ isDyingNatSession(@onNull ConntrackMessage msg)152 public static boolean isDyingNatSession(@NonNull ConntrackMessage msg) { 153 if (msg.getMessageType() != NetlinkConstants.IPCTNL_MSG_CT_DELETE) return false; 154 if (msg.tupleOrig == null) return false; 155 if (msg.tupleReply == null) return false; 156 if (msg.timeoutSec != 0) return false; 157 if ((msg.status & DYING_MASK) != DYING_MASK) return false; 158 159 return true; 160 } 161 } 162 163 /** 164 * A callback to caller for conntrack event. 165 */ 166 public interface ConntrackEventConsumer { 167 /** 168 * Every conntrack event received on the netlink socket is passed in 169 * here. 170 */ accept(@onNull ConntrackEvent event)171 void accept(@NonNull ConntrackEvent event); 172 } 173 174 private final ConntrackEventConsumer mConsumer; 175 ConntrackMonitor(@onNull Handler h, @NonNull SharedLog log, @NonNull ConntrackEventConsumer cb)176 public ConntrackMonitor(@NonNull Handler h, @NonNull SharedLog log, 177 @NonNull ConntrackEventConsumer cb) { 178 super(h, log, TAG, OsConstants.NETLINK_NETFILTER, NF_NETLINK_CONNTRACK_NEW 179 | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); 180 mConsumer = cb; 181 } 182 183 @Override processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs)184 public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) { 185 if (!(nlMsg instanceof ConntrackMessage)) { 186 mLog.e("non-conntrack msg: " + nlMsg); 187 return; 188 } 189 190 final ConntrackMessage conntrackMsg = (ConntrackMessage) nlMsg; 191 if (!(ConntrackEvent.isEstablishedNatSession(conntrackMsg) 192 || ConntrackEvent.isDyingNatSession(conntrackMsg))) { 193 return; 194 } 195 196 mConsumer.accept(new ConntrackEvent(conntrackMsg)); 197 } 198 } 199