diff --git a/pyverbs/device.pxd b/pyverbs/device.pxd
index c9e346539e58..a7089972985b 100644
--- a/pyverbs/device.pxd
+++ b/pyverbs/device.pxd
@@ -47,3 +47,6 @@ cdef class DM(PyverbsCM):
     cdef object dm_mrs
     cdef object context
     cdef add_ref(self, obj)
+
+cdef class PortAttr(PyverbsObject):
+    cdef v.ibv_port_attr attr
diff --git a/pyverbs/device.pyx b/pyverbs/device.pyx
index 7bfcda8bc177..757eebf67c48 100644
--- a/pyverbs/device.pyx
+++ b/pyverbs/device.pyx
@@ -11,6 +11,7 @@ import weakref
 from .pyverbs_error import PyverbsRDMAError, PyverbsError
 from .pyverbs_error import PyverbsUserError
 from pyverbs.base import PyverbsRDMAErrno
+cimport pyverbs.libibverbs_enums as e
 cimport pyverbs.libibverbs as v
 from pyverbs.addr cimport GID
 from pyverbs.mr import DMMR
@@ -165,6 +166,18 @@ cdef class Context(PyverbsCM):
                                                                    format(idx=index, port=port_num))
         return gid
 
+    def query_port(self, unsigned int port_num):
+        """
+        Query port <port_num> of the device and returns its attributes.
+        :param port_num: Port number to query
+        :return: PortAttr object on success
+        """
+        port_attrs = PortAttr()
+        rc = v.ibv_query_port(self.context, port_num, &port_attrs.attr)
+        if rc != 0:
+            raise PyverbsRDMAErrno('Failed to query port {p}'.format(p=port_num))
+        return port_attrs
+
     cdef add_ref(self, obj):
         if isinstance(obj, PD):
             self.pds.add(obj)
@@ -585,6 +598,99 @@ cdef class DM(PyverbsCM):
         return res
 
 
+cdef class PortAttr(PyverbsObject):
+    @property
+    def state(self):
+        return self.attr.state
+    @property
+    def max_mtu(self):
+        return self.attr.max_mtu
+    @property
+    def active_mtu(self):
+        return self.attr.active_mtu
+    @property
+    def gid_tbl_len(self):
+        return self.attr.gid_tbl_len
+    @property
+    def port_cap_flags(self):
+        return self.attr.port_cap_flags
+    @property
+    def max_msg_sz(self):
+        return self.attr.max_msg_sz
+    @property
+    def bad_pkey_cntr(self):
+        return self.attr.bad_pkey_cntr
+    @property
+    def qkey_viol_cntr(self):
+        return self.attr.qkey_viol_cntr
+    @property
+    def pkey_tbl_len(self):
+        return self.attr.pkey_tbl_len
+    @property
+    def lid(self):
+        return self.attr.lid
+    @property
+    def sm_lid(self):
+        return self.attr.sm_lid
+    @property
+    def lmc(self):
+        return self.attr.lmc
+    @property
+    def max_vl_num(self):
+        return self.attr.max_vl_num
+    @property
+    def sm_sl(self):
+        return self.attr.sm_sl
+    @property
+    def subnet_timeout(self):
+        return self.attr.subnet_timeout
+    @property
+    def init_type_reply(self):
+        return self.attr.init_type_reply
+    @property
+    def active_width(self):
+        return self.attr.active_width
+    @property
+    def active_speed(self):
+        return self.attr.active_speed
+    @property
+    def phys_state(self):
+        return self.attr.phys_state
+    @property
+    def link_layer(self):
+        return self.attr.link_layer
+    @property
+    def flags(self):
+        return self.attr.flags
+    @property
+    def port_cap_flags2(self):
+        return self.attr.port_cap_flags2
+
+    def __str__(self):
+        print_format = '{:<24}: {:<20}\n'
+        return print_format.format('Port state', port_state_to_str(self.attr.state)) +\
+            print_format.format('Max MTU', translate_mtu(self.attr.max_mtu)) +\
+            print_format.format('Active MTU', translate_mtu(self.attr.active_mtu)) +\
+            print_format.format('SM lid', self.attr.sm_lid) +\
+            print_format.format('Port lid', self.attr.lid) +\
+            print_format.format('lmc', hex(self.attr.lmc)) +\
+            print_format.format('Link layer', translate_link_layer(self.attr.link_layer)) +\
+            print_format.format('Max message size', hex(self.attr.max_msg_sz)) +\
+            print_format.format('Port cap flags', translate_port_cap_flags(self.attr.port_cap_flags)) +\
+            print_format.format('Port cap flags 2', translate_port_cap_flags2(self.attr.port_cap_flags2)) +\
+            print_format.format('max VL num', self.attr.max_vl_num) +\
+            print_format.format('Bad Pkey counter', self.attr.bad_pkey_cntr) +\
+            print_format.format('Qkey violations counter', self.attr.qkey_viol_cntr) +\
+            print_format.format('GID table len', self.attr.gid_tbl_len) +\
+            print_format.format('Pkey table len', self.attr.pkey_tbl_len) +\
+            print_format.format('SM sl', self.attr.sm_sl) +\
+            print_format.format('Subnet timeout', self.attr.subnet_timeout) +\
+            print_format.format('Init type reply', self.attr.init_type_reply) +\
+            print_format.format('Active width', width_to_str(self.attr.active_width)) +\
+            print_format.format('Active speed', speed_to_str(self.attr.active_speed)) +\
+            print_format.format('Phys state', phys_state_to_str(self.attr.phys_state)) +\
+            print_format.format('Flags', self.attr.flags)
+
 def guid_format(num):
     """
     Get GUID representation of the given number, including change of endianness.
@@ -608,6 +714,96 @@ def translate_node_type(node_type):
 def guid_to_hex(node_guid):
     return hex(node_guid).replace('L', '').replace('0x', '')
 
+def port_state_to_str(port_state):
+    l = {0: 'NOP', 1: 'Down', 2: 'Init', 3: 'Armed',4: 'Active',
+         5: 'Active defer'}
+    try:
+        return '{s} ({n})'.format(s=l[port_state], n=port_state)
+    except:
+        return 'Invalid state'
+
+def translate_mtu(mtu):
+    l = {1: 256, 2: 512, 3: 1024, 4: 2048, 5: 4096}
+    try:
+        return '{s} ({n})'.format(s=l[mtu], n=mtu)
+    except:
+        return 'Invalid MTU'
+
+def translate_link_layer(ll):
+    l = {0: 'Unspecified', 1:'InfiniBand', 2:'Ethernet'}
+    try:
+        return l[ll]
+    except:
+        return 'Invalid link layer {ll}'.format(ll=ll)
+
+def translate_port_cap_flags(flags):
+    l = {e.IBV_PORT_SM: 'IBV_PORT_SM',
+         e.IBV_PORT_NOTICE_SUP: 'IBV_PORT_NOTICE_SUP',
+         e.IBV_PORT_TRAP_SUP: 'IBV_PORT_TRAP_SUP',
+         e.IBV_PORT_OPT_IPD_SUP: 'IBV_PORT_OPT_IPD_SUP',
+         e.IBV_PORT_AUTO_MIGR_SUP: 'IBV_PORT_AUTO_MIGR_SUP',
+         e.IBV_PORT_SL_MAP_SUP: 'IBV_PORT_SL_MAP_SUP',
+         e.IBV_PORT_MKEY_NVRAM: 'IBV_PORT_MKEY_NVRAM',
+         e.IBV_PORT_PKEY_NVRAM: 'IBV_PORT_PKEY_NVRAM',
+         e.IBV_PORT_LED_INFO_SUP: 'IBV_PORT_LED_INFO_SUP',
+         e.IBV_PORT_SYS_IMAGE_GUID_SUP: 'IBV_PORT_SYS_IMAGE_GUID_SUP',
+         e.IBV_PORT_PKEY_SW_EXT_PORT_TRAP_SUP: 'IBV_PORT_PKEY_SW_EXT_PORT_TRAP_SUP',
+         e.IBV_PORT_EXTENDED_SPEEDS_SUP: 'IBV_PORT_EXTENDED_SPEEDS_SUP',
+         e.IBV_PORT_CAP_MASK2_SUP: 'IBV_PORT_CAP_MASK2_SUP',
+         e.IBV_PORT_CM_SUP: 'IBV_PORT_CM_SUP',
+         e.IBV_PORT_SNMP_TUNNEL_SUP: 'IBV_PORT_SNMP_TUNNEL_SUP',
+         e.IBV_PORT_REINIT_SUP: 'IBV_PORT_REINIT_SUP',
+         e.IBV_PORT_DEVICE_MGMT_SUP: 'IBV_PORT_DEVICE_MGMT_SUP',
+         e.IBV_PORT_VENDOR_CLASS_SUP: 'IBV_PORT_VENDOR_CLASS_SUP',
+         e.IBV_PORT_DR_NOTICE_SUP: 'IBV_PORT_DR_NOTICE_SUP',
+         e.IBV_PORT_CAP_MASK_NOTICE_SUP: 'IBV_PORT_CAP_MASK_NOTICE_SUP',
+         e.IBV_PORT_BOOT_MGMT_SUP: 'IBV_PORT_BOOT_MGMT_SUP',
+         e.IBV_PORT_LINK_LATENCY_SUP: 'IBV_PORT_LINK_LATENCY_SUP',
+         e.IBV_PORT_CLIENT_REG_SUP: 'IBV_PORT_CLIENT_REG_SUP',
+         e.IBV_PORT_IP_BASED_GIDS: 'IBV_PORT_IP_BASED_GIDS'}
+    return str_from_flags(flags, l)
+
+def translate_port_cap_flags2(flags):
+    l = {e.IBV_PORT_SET_NODE_DESC_SUP: 'IBV_PORT_SET_NODE_DESC_SUP',
+         e.IBV_PORT_INFO_EXT_SUP: 'IBV_PORT_INFO_EXT_SUP',
+         e.IBV_PORT_VIRT_SUP: 'IBV_PORT_VIRT_SUP',
+         e.IBV_PORT_SWITCH_PORT_STATE_TABLE_SUP: 'IBV_PORT_SWITCH_PORT_STATE_TABLE_SUP',
+         e.IBV_PORT_LINK_WIDTH_2X_SUP: 'IBV_PORT_LINK_WIDTH_2X_SUP',
+         e.IBV_PORT_LINK_SPEED_HDR_SUP: 'IBV_PORT_LINK_SPEED_HDR_SUP'}
+    return str_from_flags(flags, l)
+
+def str_from_flags(flags, dictionary):
+    str_flags = ""
+    for bit in dictionary:
+        if flags & bit:
+            str_flags += dictionary[bit]
+            str_flags += ' '
+    return str_flags
+
+def phys_state_to_str(phys):
+    l =  {1: 'Sleep', 2: 'Polling', 3: 'Disabled',
+          4: 'Port configuration training', 5: 'Link up',
+          6: 'Link error recovery', 7: 'Phy test'}
+    try:
+        return '{s} ({n})'.format(s=l[phys], n=phys)
+    except:
+        return 'Invalid physical state'
+
+def width_to_str(width):
+    l = {1: '1X', 2: '4X', 4: '8X', 16: '2X'}
+    try:
+        return '{s} ({n})'.format(s=l[width], n=width)
+    except:
+        return 'Invalid width'
+
+def speed_to_str(speed):
+    l = {1: '2.5 Gbps', 2: '5.0 Gbps', 4: '5.0 Gbps', 8: '10.0 Gbps',
+         16: '14.0 Gbps', 32: '25.0 Gbps', 64: '50.0 Gbps'}
+    try:
+        return '{s} ({n})'.format(s=l[speed], n=speed)
+    except:
+        return 'Invalid speed'
+
 def get_device_list():
     """
     :return: list of IB_devices on current node
diff --git a/pyverbs/libibverbs.pxd b/pyverbs/libibverbs.pxd
index 5dd3ba013e5e..c6b564c4d487 100644
--- a/pyverbs/libibverbs.pxd
+++ b/pyverbs/libibverbs.pxd
@@ -148,6 +148,30 @@ cdef extern from 'infiniband/verbs.h':
         ibv_context     *context
         unsigned int    comp_mask
 
+    cdef struct ibv_port_attr:
+        ibv_port_state     state
+        ibv_mtu            max_mtu
+        ibv_mtu            active_mtu
+        int                     gid_tbl_len
+        unsigned int            port_cap_flags
+        unsigned int            max_msg_sz
+        unsigned int            bad_pkey_cntr
+        unsigned int            qkey_viol_cntr
+        unsigned short          pkey_tbl_len
+        unsigned short          lid
+        unsigned short          sm_lid
+        unsigned char           lmc
+        unsigned char           max_vl_num
+        unsigned char           sm_sl
+        unsigned char           subnet_timeout
+        unsigned char           init_type_reply
+        unsigned char           active_width
+        unsigned char           active_speed
+        unsigned char           phys_state
+        unsigned char           link_layer
+        unsigned char           flags
+        unsigned short          port_cap_flags2
+
     ibv_device **ibv_get_device_list(int *n)
     void ibv_free_device_list(ibv_device **list)
     ibv_context *ibv_open_device(ibv_device *device)
@@ -173,3 +197,5 @@ cdef extern from 'infiniband/verbs.h':
                          size_t length)
     int ibv_memcpy_from_dm(void *host_addr,  ibv_dm *dm, unsigned long dm_offset,
                            size_t length)
+    int ibv_query_port(ibv_context *context, uint8_t port_num,
+                       ibv_port_attr *port_attr)
diff --git a/pyverbs/libibverbs_enums.pxd b/pyverbs/libibverbs_enums.pxd
index d6e81199fae3..2525e869a201 100644
--- a/pyverbs/libibverbs_enums.pxd
+++ b/pyverbs/libibverbs_enums.pxd
@@ -3,6 +3,11 @@
 
 cdef extern from '<infiniband/verbs.h>':
 
+    cpdef enum:
+        IBV_LINK_LAYER_UNSPECIFIED
+        IBV_LINK_LAYER_INFINIBAND
+        IBV_LINK_LAYER_ETHERNET
+
     cpdef enum ibv_atomic_cap:
         IBV_ATOMIC_NONE
         IBV_ATOMIC_HCA
@@ -29,6 +34,7 @@ cdef extern from '<infiniband/verbs.h>':
         IBV_PORT_SYS_IMAGE_GUID_SUP         = 1 << 11
         IBV_PORT_PKEY_SW_EXT_PORT_TRAP_SUP  = 1 << 12
         IBV_PORT_EXTENDED_SPEEDS_SUP        = 1 << 14
+        IBV_PORT_CAP_MASK2_SUP              = 1 << 15,
         IBV_PORT_CM_SUP                     = 1 << 16
         IBV_PORT_SNMP_TUNNEL_SUP            = 1 << 17
         IBV_PORT_REINIT_SUP                 = 1 << 18
@@ -41,6 +47,14 @@ cdef extern from '<infiniband/verbs.h>':
         IBV_PORT_CLIENT_REG_SUP             = 1 << 25
         IBV_PORT_IP_BASED_GIDS              = 1 << 26
 
+    cpdef enum ibv_port_cap_flags2:
+        IBV_PORT_SET_NODE_DESC_SUP              = 1 << 0
+        IBV_PORT_INFO_EXT_SUP                   = 1 << 1
+        IBV_PORT_VIRT_SUP                       = 1 << 2
+        IBV_PORT_SWITCH_PORT_STATE_TABLE_SUP    = 1 << 3
+        IBV_PORT_LINK_WIDTH_2X_SUP              = 1 << 4
+        IBV_PORT_LINK_SPEED_HDR_SUP             = 1 << 5
+
     cpdef enum ibv_mtu:
         IBV_MTU_256     = 1
         IBV_MTU_512     = 2
